mirror of
https://github.com/esphome/esphome.git
synced 2025-08-23 10:39:25 +00:00
Compare commits
1 Commits
integratio
...
optimize_e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fb1c2467b9 |
@@ -1 +1 @@
|
||||
0440e35cf89a49e8a35fd3690ed453a72b7b6f61b9d346ced6140e1c0d39dff6
|
||||
6af8b429b94191fe8e239fcb3b73f7982d0266cb5b05ffbc81edaeac1bc8c273
|
||||
|
30
.github/workflows/auto-label-pr.yml
vendored
30
.github/workflows/auto-label-pr.yml
vendored
@@ -105,9 +105,7 @@ jobs:
|
||||
|
||||
// Calculate data from PR files
|
||||
const changedFiles = prFiles.map(file => file.filename);
|
||||
const totalAdditions = prFiles.reduce((sum, file) => sum + (file.additions || 0), 0);
|
||||
const totalDeletions = prFiles.reduce((sum, file) => sum + (file.deletions || 0), 0);
|
||||
const totalChanges = totalAdditions + totalDeletions;
|
||||
const totalChanges = prFiles.reduce((sum, file) => sum + (file.additions || 0) + (file.deletions || 0), 0);
|
||||
|
||||
console.log('Current labels:', currentLabels.join(', '));
|
||||
console.log('Changed files:', changedFiles.length);
|
||||
@@ -233,21 +231,16 @@ jobs:
|
||||
// Strategy: PR size detection
|
||||
async function detectPRSize() {
|
||||
const labels = new Set();
|
||||
const testChanges = prFiles
|
||||
.filter(file => file.filename.startsWith('tests/'))
|
||||
.reduce((sum, file) => sum + (file.additions || 0) + (file.deletions || 0), 0);
|
||||
|
||||
const nonTestChanges = totalChanges - testChanges;
|
||||
|
||||
if (totalChanges <= SMALL_PR_THRESHOLD) {
|
||||
labels.add('small-pr');
|
||||
return labels;
|
||||
}
|
||||
|
||||
const testAdditions = prFiles
|
||||
.filter(file => file.filename.startsWith('tests/'))
|
||||
.reduce((sum, file) => sum + (file.additions || 0), 0);
|
||||
const testDeletions = prFiles
|
||||
.filter(file => file.filename.startsWith('tests/'))
|
||||
.reduce((sum, file) => sum + (file.deletions || 0), 0);
|
||||
|
||||
const nonTestChanges = (totalAdditions - testAdditions) - (totalDeletions - testDeletions);
|
||||
|
||||
// Don't add too-big if mega-pr label is already present
|
||||
if (nonTestChanges > TOO_BIG_THRESHOLD && !isMegaPR) {
|
||||
labels.add('too-big');
|
||||
@@ -382,7 +375,7 @@ jobs:
|
||||
const labels = new Set();
|
||||
|
||||
// Check for missing tests
|
||||
if ((allLabels.has('new-component') || allLabels.has('new-platform') || allLabels.has('new-feature')) && !allLabels.has('has-tests')) {
|
||||
if ((allLabels.has('new-component') || allLabels.has('new-platform')) && !allLabels.has('has-tests')) {
|
||||
labels.add('needs-tests');
|
||||
}
|
||||
|
||||
@@ -419,13 +412,10 @@ jobs:
|
||||
|
||||
// Too big message
|
||||
if (finalLabels.includes('too-big')) {
|
||||
const testAdditions = prFiles
|
||||
const testChanges = prFiles
|
||||
.filter(file => file.filename.startsWith('tests/'))
|
||||
.reduce((sum, file) => sum + (file.additions || 0), 0);
|
||||
const testDeletions = prFiles
|
||||
.filter(file => file.filename.startsWith('tests/'))
|
||||
.reduce((sum, file) => sum + (file.deletions || 0), 0);
|
||||
const nonTestChanges = (totalAdditions - testAdditions) - (totalDeletions - testDeletions);
|
||||
.reduce((sum, file) => sum + (file.additions || 0) + (file.deletions || 0), 0);
|
||||
const nonTestChanges = totalChanges - testChanges;
|
||||
|
||||
const tooManyLabels = finalLabels.length > MAX_LABELS;
|
||||
const tooManyChanges = nonTestChanges > TOO_BIG_THRESHOLD;
|
||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -156,7 +156,7 @@ jobs:
|
||||
. venv/bin/activate
|
||||
pytest -vv --cov-report=xml --tb=native -n auto tests --ignore=tests/integration/
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5.5.0
|
||||
uses: codecov/codecov-action@v5.4.3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Save Python virtual environment cache
|
||||
|
24
.github/workflows/needs-docs.yml
vendored
Normal file
24
.github/workflows/needs-docs.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Needs Docs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, unlabeled]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for needs-docs label
|
||||
uses: actions/github-script@v7.0.1
|
||||
with:
|
||||
script: |
|
||||
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number
|
||||
});
|
||||
const needsDocs = labels.find(label => label.name === 'needs-docs');
|
||||
if (needsDocs) {
|
||||
core.setFailed('Pull request needs docs');
|
||||
}
|
30
.github/workflows/status-check-labels.yml
vendored
30
.github/workflows/status-check-labels.yml
vendored
@@ -1,30 +0,0 @@
|
||||
name: Status check labels
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, unlabeled]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check ${{ matrix.label }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
label:
|
||||
- needs-docs
|
||||
- merge-after-release
|
||||
steps:
|
||||
- name: Check for ${{ matrix.label }} label
|
||||
uses: actions/github-script@v7.0.1
|
||||
with:
|
||||
script: |
|
||||
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number
|
||||
});
|
||||
const hasLabel = labels.find(label => label.name === '${{ matrix.label }}');
|
||||
if (hasLabel) {
|
||||
core.setFailed('Pull request cannot be merged, it is labeled as ${{ matrix.label }}');
|
||||
}
|
@@ -476,7 +476,7 @@ def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int
|
||||
from esphome.components.api.client import run_logs
|
||||
|
||||
return run_logs(config, addresses_to_use)
|
||||
if get_port_type(port) in ("NETWORK", "MQTT") and "mqtt" in config:
|
||||
if get_port_type(port) == "MQTT" and "mqtt" in config:
|
||||
from esphome import mqtt
|
||||
|
||||
return mqtt.show_logs(
|
||||
@@ -524,13 +524,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
|
||||
@@ -941,17 +934,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",
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -89,7 +89,7 @@ void AGS10Component::dump_config() {
|
||||
bool AGS10Component::new_i2c_address(uint8_t newaddress) {
|
||||
uint8_t rev_newaddress = ~newaddress;
|
||||
std::array<uint8_t, 5> data{newaddress, rev_newaddress, newaddress, rev_newaddress, 0};
|
||||
data[4] = crc8(data.data(), 4, 0xFF, 0x31, true);
|
||||
data[4] = calc_crc8_(data, 4);
|
||||
if (!this->write_bytes(REG_ADDRESS, data)) {
|
||||
this->error_code_ = COMMUNICATION_FAILED;
|
||||
this->status_set_warning();
|
||||
@@ -109,7 +109,7 @@ bool AGS10Component::set_zero_point_with_current_resistance() { return this->set
|
||||
|
||||
bool AGS10Component::set_zero_point_with(uint16_t value) {
|
||||
std::array<uint8_t, 5> data{0x00, 0x0C, (uint8_t) ((value >> 8) & 0xFF), (uint8_t) (value & 0xFF), 0};
|
||||
data[4] = crc8(data.data(), 4, 0xFF, 0x31, true);
|
||||
data[4] = calc_crc8_(data, 4);
|
||||
if (!this->write_bytes(REG_CALIBRATION, data)) {
|
||||
this->error_code_ = COMMUNICATION_FAILED;
|
||||
this->status_set_warning();
|
||||
@@ -184,7 +184,7 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
|
||||
auto res = *data;
|
||||
auto crc_byte = res[len];
|
||||
|
||||
if (crc_byte != crc8(res.data(), len, 0xFF, 0x31, true)) {
|
||||
if (crc_byte != calc_crc8_(res, len)) {
|
||||
this->error_code_ = CRC_CHECK_FAILED;
|
||||
ESP_LOGE(TAG, "Reading AGS10 version failed: crc error!");
|
||||
return optional<std::array<uint8_t, N>>();
|
||||
@@ -192,5 +192,20 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
template<size_t N> uint8_t AGS10Component::calc_crc8_(std::array<uint8_t, N> dat, uint8_t num) {
|
||||
uint8_t i, byte1, crc = 0xFF;
|
||||
for (byte1 = 0; byte1 < num; byte1++) {
|
||||
crc ^= (dat[byte1]);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (crc & 0x80) {
|
||||
crc = (crc << 1) ^ 0x31;
|
||||
} else {
|
||||
crc = (crc << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
} // namespace ags10
|
||||
} // namespace esphome
|
||||
|
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ags10 {
|
||||
@@ -99,6 +99,16 @@ class AGS10Component : public PollingComponent, public i2c::I2CDevice {
|
||||
* Read, checks and returns data from the sensor.
|
||||
*/
|
||||
template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register);
|
||||
|
||||
/**
|
||||
* Calculates CRC8 value.
|
||||
*
|
||||
* CRC8 calculation, initial value: 0xFF, polynomial: 0x31 (x8+ x5+ x4+1)
|
||||
*
|
||||
* @param[in] dat the data buffer
|
||||
* @param num number of bytes in the buffer
|
||||
*/
|
||||
template<size_t N> uint8_t calc_crc8_(std::array<uint8_t, N> dat, uint8_t num);
|
||||
};
|
||||
|
||||
template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>, public Parented<AGS10Component> {
|
||||
|
@@ -18,6 +18,6 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||
|
@@ -29,6 +29,22 @@ namespace am2315c {
|
||||
|
||||
static const char *const TAG = "am2315c";
|
||||
|
||||
uint8_t AM2315C::crc8_(uint8_t *data, uint8_t len) {
|
||||
uint8_t crc = 0xFF;
|
||||
while (len--) {
|
||||
crc ^= *data++;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
if (crc & 0x80) {
|
||||
crc <<= 1;
|
||||
crc ^= 0x31;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool AM2315C::reset_register_(uint8_t reg) {
|
||||
// code based on demo code sent by www.aosong.com
|
||||
// no further documentation.
|
||||
@@ -70,7 +86,7 @@ bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) {
|
||||
humidity = raw * 9.5367431640625e-5;
|
||||
raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
|
||||
temperature = raw * 1.9073486328125e-4 - 50;
|
||||
return crc8(data, 6, 0xFF, 0x31, true) == data[6];
|
||||
return this->crc8_(data, 6) == data[6];
|
||||
}
|
||||
|
||||
void AM2315C::setup() {
|
||||
|
@@ -21,9 +21,9 @@
|
||||
// SOFTWARE.
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace am2315c {
|
||||
@@ -39,6 +39,7 @@ class AM2315C : public PollingComponent, public i2c::I2CDevice {
|
||||
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
|
||||
|
||||
protected:
|
||||
uint8_t crc8_(uint8_t *data, uint8_t len);
|
||||
bool convert_(uint8_t *data, float &humidity, float &temperature);
|
||||
bool reset_register_(uint8_t reg);
|
||||
|
||||
|
@@ -122,8 +122,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_CUSTOM_SERVICES, default=False): cv.boolean,
|
||||
cv.Optional(CONF_HOMEASSISTANT_SERVICES, default=False): cv.boolean,
|
||||
cv.Optional(CONF_HOMEASSISTANT_STATES, default=False): cv.boolean,
|
||||
cv.Optional(CONF_HOMEASSISTANT_SERVICES, default=False): cv.boolean,
|
||||
cv.Optional(CONF_HOMEASSISTANT_STATES, default=False): cv.boolean,
|
||||
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
|
||||
single=True
|
||||
),
|
||||
@@ -323,7 +321,6 @@ HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA,
|
||||
)
|
||||
async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
|
||||
cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
|
||||
serv = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
||||
cg.add(var.set_service("esphome.tag_scanned"))
|
||||
|
@@ -465,7 +465,9 @@ uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *
|
||||
resp.cold_white = values.get_cold_white();
|
||||
resp.warm_white = values.get_warm_white();
|
||||
if (light->supports_effects()) {
|
||||
resp.set_effect(light->get_effect_name_ref());
|
||||
// get_effect_name() returns temporary std::string - must store it
|
||||
std::string effect_name = light->get_effect_name();
|
||||
resp.set_effect(StringRef(effect_name));
|
||||
}
|
||||
return fill_and_encode_entity_state(light, resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||
}
|
||||
@@ -1423,7 +1425,9 @@ bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
|
||||
static constexpr auto ESPHOME_VERSION_REF = StringRef::from_lit(ESPHOME_VERSION);
|
||||
resp.set_esphome_version(ESPHOME_VERSION_REF);
|
||||
|
||||
resp.set_compilation_time(App.get_compilation_time_ref());
|
||||
// get_compilation_time() returns temporary std::string - must store it
|
||||
std::string compilation_time = App.get_compilation_time();
|
||||
resp.set_compilation_time(StringRef(compilation_time));
|
||||
|
||||
// Compile-time StringRef constants for manufacturers
|
||||
#if defined(USE_ESP8266) || defined(USE_ESP32)
|
||||
|
@@ -44,7 +44,7 @@ static constexpr size_t MAX_PACKETS_PER_BATCH = 64; // ESP32 has 8KB+ stack, HO
|
||||
static constexpr size_t MAX_PACKETS_PER_BATCH = 32; // ESP8266/RP2040/etc have smaller stacks
|
||||
#endif
|
||||
|
||||
class APIConnection final : public APIServerConnection {
|
||||
class APIConnection : public APIServerConnection {
|
||||
public:
|
||||
friend class APIServer;
|
||||
friend class ListEntitiesIterator;
|
||||
@@ -301,15 +301,9 @@ class APIConnection final : public APIServerConnection {
|
||||
APIConnection *conn, uint32_t remaining_size, bool is_single) {
|
||||
// Set common fields that are shared by all entity types
|
||||
msg.key = entity->get_object_id_hash();
|
||||
// Try to use static reference first to avoid allocation
|
||||
StringRef static_ref = entity->get_object_id_ref_for_api_();
|
||||
if (!static_ref.empty()) {
|
||||
msg.set_object_id(static_ref);
|
||||
} else {
|
||||
// Dynamic case - need to allocate
|
||||
std::string object_id = entity->get_object_id();
|
||||
msg.set_object_id(StringRef(object_id));
|
||||
}
|
||||
// IMPORTANT: get_object_id() may return a temporary std::string
|
||||
std::string object_id = entity->get_object_id();
|
||||
msg.set_object_id(StringRef(object_id));
|
||||
|
||||
if (entity->has_own_name()) {
|
||||
msg.set_name(entity->get_name());
|
||||
|
@@ -104,9 +104,9 @@ class APIFrameHelper {
|
||||
// The buffer contains all messages with appropriate padding before each
|
||||
virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) = 0;
|
||||
// Get the frame header padding required by this protocol
|
||||
uint8_t frame_header_padding() const { return frame_header_padding_; }
|
||||
virtual uint8_t frame_header_padding() = 0;
|
||||
// Get the frame footer size required by this protocol
|
||||
uint8_t frame_footer_size() const { return frame_footer_size_; }
|
||||
virtual uint8_t frame_footer_size() = 0;
|
||||
// Check if socket has data ready to read
|
||||
bool is_socket_ready() const { return socket_ != nullptr && socket_->ready(); }
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace esphome::api {
|
||||
|
||||
class APINoiseFrameHelper final : public APIFrameHelper {
|
||||
class APINoiseFrameHelper : public APIFrameHelper {
|
||||
public:
|
||||
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx,
|
||||
const ClientInfo *client_info)
|
||||
@@ -25,6 +25,10 @@ class APINoiseFrameHelper final : public APIFrameHelper {
|
||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||
APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override;
|
||||
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
|
||||
// Get the frame header padding required by this protocol
|
||||
uint8_t frame_header_padding() override { return frame_header_padding_; }
|
||||
// Get the frame footer size required by this protocol
|
||||
uint8_t frame_footer_size() override { return frame_footer_size_; }
|
||||
|
||||
protected:
|
||||
APIError state_action_();
|
||||
|
@@ -235,8 +235,8 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer
|
||||
|
||||
for (const auto &packet : packets) {
|
||||
// Calculate varint sizes for header layout
|
||||
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.payload_size));
|
||||
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.message_type));
|
||||
uint8_t size_varint_len = api::ProtoSize::varint(packet.payload_size);
|
||||
uint8_t type_varint_len = api::ProtoSize::varint(packet.message_type);
|
||||
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
|
||||
|
||||
// Calculate where to start writing the header
|
||||
@@ -271,9 +271,8 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer
|
||||
buf_start[header_offset] = 0x00; // indicator
|
||||
|
||||
// Encode varints directly into buffer
|
||||
ProtoVarInt(packet.payload_size).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
|
||||
ProtoVarInt(packet.message_type)
|
||||
.encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
|
||||
encode_varint_unchecked(buf_start + header_offset + 1, packet.payload_size);
|
||||
encode_varint_unchecked(buf_start + header_offset + 1 + size_varint_len, packet.message_type);
|
||||
|
||||
// Add iovec for this packet (header + payload)
|
||||
size_t packet_len = static_cast<size_t>(total_header_len + packet.payload_size);
|
||||
|
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace esphome::api {
|
||||
|
||||
class APIPlaintextFrameHelper final : public APIFrameHelper {
|
||||
class APIPlaintextFrameHelper : public APIFrameHelper {
|
||||
public:
|
||||
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket, const ClientInfo *client_info)
|
||||
: APIFrameHelper(std::move(socket), client_info) {
|
||||
@@ -22,6 +22,9 @@ class APIPlaintextFrameHelper final : public APIFrameHelper {
|
||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||
APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override;
|
||||
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
|
||||
uint8_t frame_header_padding() override { return frame_header_padding_; }
|
||||
// Get the frame footer size required by this protocol
|
||||
uint8_t frame_footer_size() override { return frame_footer_size_; }
|
||||
|
||||
protected:
|
||||
APIError try_read_frame_(std::vector<uint8_t> *frame);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,70 +8,74 @@ namespace esphome::api {
|
||||
static const char *const TAG = "api.proto";
|
||||
|
||||
void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
const uint8_t *ptr = buffer;
|
||||
const uint8_t *end = buffer + length;
|
||||
|
||||
while (ptr < end) {
|
||||
uint32_t i = 0;
|
||||
bool error = false;
|
||||
while (i < length) {
|
||||
uint32_t consumed;
|
||||
|
||||
// Parse field header
|
||||
auto res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
|
||||
auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
|
||||
if (!res.has_value()) {
|
||||
ESP_LOGV(TAG, "Invalid field start at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
ESP_LOGV(TAG, "Invalid field start at %" PRIu32, i);
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t tag = res->as_uint32();
|
||||
uint32_t field_type = tag & 0b111;
|
||||
uint32_t field_id = tag >> 3;
|
||||
ptr += consumed;
|
||||
uint32_t field_type = (res->as_uint32()) & 0b111;
|
||||
uint32_t field_id = (res->as_uint32()) >> 3;
|
||||
i += consumed;
|
||||
|
||||
switch (field_type) {
|
||||
case 0: { // VarInt
|
||||
res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
|
||||
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
|
||||
if (!res.has_value()) {
|
||||
ESP_LOGV(TAG, "Invalid VarInt at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
ESP_LOGV(TAG, "Invalid VarInt at %" PRIu32, i);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
if (!this->decode_varint(field_id, *res)) {
|
||||
ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32());
|
||||
}
|
||||
ptr += consumed;
|
||||
i += consumed;
|
||||
break;
|
||||
}
|
||||
case 2: { // Length-delimited
|
||||
res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
|
||||
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
|
||||
if (!res.has_value()) {
|
||||
ESP_LOGV(TAG, "Invalid Length Delimited at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
ESP_LOGV(TAG, "Invalid Length Delimited at %" PRIu32, i);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
uint32_t field_length = res->as_uint32();
|
||||
ptr += consumed;
|
||||
if (ptr + field_length > end) {
|
||||
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
i += consumed;
|
||||
if (field_length > length - i) {
|
||||
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %" PRIu32, i);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
if (!this->decode_length(field_id, ProtoLengthDelimited(ptr, field_length))) {
|
||||
if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) {
|
||||
ESP_LOGV(TAG, "Cannot decode Length Delimited field %" PRIu32 "!", field_id);
|
||||
}
|
||||
ptr += field_length;
|
||||
i += field_length;
|
||||
break;
|
||||
}
|
||||
case 5: { // 32-bit
|
||||
if (ptr + 4 > end) {
|
||||
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
if (length - i < 4) {
|
||||
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %" PRIu32, i);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
uint32_t val = encode_uint32(ptr[3], ptr[2], ptr[1], ptr[0]);
|
||||
uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
|
||||
if (!this->decode_32bit(field_id, Proto32Bit(val))) {
|
||||
ESP_LOGV(TAG, "Cannot decode 32-bit field %" PRIu32 " with value %" PRIu32 "!", field_id, val);
|
||||
}
|
||||
ptr += 4;
|
||||
i += 4;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGV(TAG, "Invalid field type %u at offset %ld", field_type, (long) (ptr - buffer));
|
||||
return;
|
||||
ESP_LOGV(TAG, "Invalid field type at %" PRIu32, i);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -124,34 +124,6 @@ class ProtoVarInt {
|
||||
// with ZigZag encoding
|
||||
return decode_zigzag64(this->value_);
|
||||
}
|
||||
/**
|
||||
* Encode the varint value to a pre-allocated buffer without bounds checking.
|
||||
*
|
||||
* @param buffer The pre-allocated buffer to write the encoded varint to
|
||||
* @param len The size of the buffer in bytes
|
||||
*
|
||||
* @note The caller is responsible for ensuring the buffer is large enough
|
||||
* to hold the encoded value. Use ProtoSize::varint() to calculate
|
||||
* the exact size needed before calling this method.
|
||||
* @note No bounds checking is performed for performance reasons.
|
||||
*/
|
||||
void encode_to_buffer_unchecked(uint8_t *buffer, size_t len) {
|
||||
uint64_t val = this->value_;
|
||||
if (val <= 0x7F) {
|
||||
buffer[0] = val;
|
||||
return;
|
||||
}
|
||||
size_t i = 0;
|
||||
while (val && i < len) {
|
||||
uint8_t temp = val & 0x7F;
|
||||
val >>= 7;
|
||||
if (val) {
|
||||
buffer[i++] = temp | 0x80;
|
||||
} else {
|
||||
buffer[i++] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
void encode(std::vector<uint8_t> &out) {
|
||||
uint64_t val = this->value_;
|
||||
if (val <= 0x7F) {
|
||||
@@ -330,6 +302,28 @@ class ProtoWriteBuffer {
|
||||
std::vector<uint8_t> *buffer_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Encode a uint16_t value as a varint directly to a buffer without bounds checking
|
||||
*
|
||||
* @param buffer The pre-allocated buffer to write the encoded varint to
|
||||
* @param value The uint16_t value to encode (0-65535)
|
||||
*
|
||||
* @note The caller is responsible for ensuring the buffer is large enough (max 3 bytes for uint16_t)
|
||||
* @note No bounds checking is performed for performance reasons
|
||||
*/
|
||||
inline void encode_varint_unchecked(uint8_t *buffer, uint16_t value) {
|
||||
if (value < 128) {
|
||||
buffer[0] = value;
|
||||
} else if (value < 16384) {
|
||||
buffer[0] = (value & 0x7F) | 0x80;
|
||||
buffer[1] = value >> 7;
|
||||
} else {
|
||||
buffer[0] = (value & 0x7F) | 0x80;
|
||||
buffer[1] = ((value >> 7) & 0x7F) | 0x80;
|
||||
buffer[2] = value >> 14;
|
||||
}
|
||||
}
|
||||
|
||||
// Forward declaration
|
||||
class ProtoSize;
|
||||
|
||||
@@ -386,6 +380,33 @@ class ProtoSize {
|
||||
|
||||
uint32_t get_size() const { return total_size_; }
|
||||
|
||||
/**
|
||||
* @brief Calculates the size in bytes needed to encode a uint8_t value as a varint
|
||||
*
|
||||
* @param value The uint8_t value to calculate size for
|
||||
* @return The number of bytes needed to encode the value (1 or 2)
|
||||
*/
|
||||
static constexpr uint8_t varint(uint8_t value) {
|
||||
// For uint8_t (0-255), we need at most 2 bytes
|
||||
return (value < 128) ? 1 : 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the size in bytes needed to encode a uint16_t value as a varint
|
||||
*
|
||||
* @param value The uint16_t value to calculate size for
|
||||
* @return The number of bytes needed to encode the value (1-3)
|
||||
*/
|
||||
static constexpr uint8_t varint(uint16_t value) {
|
||||
// For uint16_t (0-65535), we need at most 3 bytes
|
||||
if (value < 128)
|
||||
return 1; // 7 bits
|
||||
else if (value < 16384)
|
||||
return 2; // 14 bits
|
||||
else
|
||||
return 3; // 15-16 bits
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
|
||||
*
|
||||
@@ -395,11 +416,9 @@ class ProtoSize {
|
||||
static constexpr uint32_t varint(uint32_t value) {
|
||||
// Optimized varint size calculation using leading zeros
|
||||
// Each 7 bits requires one byte in the varint encoding
|
||||
if (value < 128)
|
||||
if (value < 128) {
|
||||
return 1; // 7 bits, common case for small values
|
||||
|
||||
// For larger values, count bytes needed based on the position of the highest bit set
|
||||
if (value < 16384) {
|
||||
} else if (value < 16384) {
|
||||
return 2; // 14 bits
|
||||
} else if (value < 2097152) {
|
||||
return 3; // 21 bits
|
||||
@@ -773,7 +792,7 @@ inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessa
|
||||
this->buffer_->resize(this->buffer_->size() + varint_length_bytes);
|
||||
|
||||
// Write the length varint directly
|
||||
ProtoVarInt(msg_length_bytes).encode_to_buffer_unchecked(this->buffer_->data() + begin, varint_length_bytes);
|
||||
encode_varint_unchecked(this->buffer_->data() + begin, static_cast<uint16_t>(msg_length_bytes));
|
||||
|
||||
// Now encode the message content - it will append to the buffer
|
||||
value.encode(*this);
|
||||
|
@@ -382,15 +382,20 @@ float ATM90E32Component::get_setup_priority() const { return setup_priority::IO;
|
||||
// R/C registers can conly be cleared after the LastSPIData register is updated (register 78H)
|
||||
// Peakdetect period: 05H. Bit 15:8 are PeakDet_period in ms. 7:0 are Sag_period
|
||||
// Default is 143FH (20ms, 63ms)
|
||||
uint16_t ATM90E32Component::read16_(uint16_t a_register) {
|
||||
this->enable();
|
||||
delay_microseconds_safe(1); // min delay between CS low and first SCK is 200ns - 1us is plenty
|
||||
uint16_t ATM90E32Component::read16_transaction_(uint16_t a_register) {
|
||||
uint8_t addrh = (1 << 7) | ((a_register >> 8) & 0x03);
|
||||
uint8_t addrl = (a_register & 0xFF);
|
||||
uint8_t data[4] = {addrh, addrl, 0x00, 0x00};
|
||||
this->transfer_array(data, 4);
|
||||
uint16_t output = encode_uint16(data[2], data[3]);
|
||||
ESP_LOGVV(TAG, "read16_ 0x%04" PRIX16 " output 0x%04" PRIX16, a_register, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
uint16_t ATM90E32Component::read16_(uint16_t a_register) {
|
||||
this->enable();
|
||||
delay_microseconds_safe(1); // min delay between CS low and first SCK is 200ns - 1us is plenty
|
||||
uint16_t output = this->read16_transaction_(a_register);
|
||||
delay_microseconds_safe(1); // allow the last clock to propagate before releasing CS
|
||||
this->disable();
|
||||
delay_microseconds_safe(1); // meet minimum CS high time before next transaction
|
||||
@@ -398,8 +403,14 @@ uint16_t ATM90E32Component::read16_(uint16_t a_register) {
|
||||
}
|
||||
|
||||
int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) {
|
||||
const uint16_t val_h = this->read16_(addr_h);
|
||||
const uint16_t val_l = this->read16_(addr_l);
|
||||
this->enable();
|
||||
delay_microseconds_safe(1);
|
||||
const uint16_t val_h = this->read16_transaction_(addr_h);
|
||||
delay_microseconds_safe(1);
|
||||
const uint16_t val_l = this->read16_transaction_(addr_l);
|
||||
delay_microseconds_safe(1);
|
||||
this->disable();
|
||||
delay_microseconds_safe(1);
|
||||
const int32_t val = (val_h << 16) | val_l;
|
||||
|
||||
ESP_LOGVV(TAG,
|
||||
|
@@ -140,6 +140,7 @@ class ATM90E32Component : public PollingComponent,
|
||||
number::Number *ref_currents_[3]{nullptr, nullptr, nullptr};
|
||||
#endif
|
||||
uint16_t read16_(uint16_t a_register);
|
||||
uint16_t read16_transaction_(uint16_t a_register);
|
||||
int read32_(uint16_t addr_h, uint16_t addr_l);
|
||||
void write16_(uint16_t a_register, uint16_t val, bool validate = true);
|
||||
float get_local_phase_voltage_(uint8_t phase);
|
||||
|
@@ -7,19 +7,6 @@ namespace binary_sensor {
|
||||
|
||||
static const char *const TAG = "binary_sensor";
|
||||
|
||||
// Function implementation of LOG_BINARY_SENSOR macro to reduce code size
|
||||
void log_binary_sensor(const char *tag, const char *prefix, const char *type, BinarySensor *obj) {
|
||||
if (obj == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
|
||||
|
||||
if (!obj->get_device_class().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void BinarySensor::publish_state(bool new_state) {
|
||||
if (this->filter_list_ == nullptr) {
|
||||
this->send_state_internal(new_state);
|
||||
|
@@ -10,10 +10,13 @@ namespace esphome {
|
||||
|
||||
namespace binary_sensor {
|
||||
|
||||
class BinarySensor;
|
||||
void log_binary_sensor(const char *tag, const char *prefix, const char *type, BinarySensor *obj);
|
||||
|
||||
#define LOG_BINARY_SENSOR(prefix, type, obj) log_binary_sensor(TAG, prefix, LOG_STR_LITERAL(type), obj)
|
||||
#define LOG_BINARY_SENSOR(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SUB_BINARY_SENSOR(name) \
|
||||
protected: \
|
||||
|
@@ -27,7 +27,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
@@ -63,6 +63,6 @@ async def to_code(config):
|
||||
)
|
||||
cg.add(var.set_char_uuid128(uuid128))
|
||||
cg.add(var.set_require_response(config[CONF_REQUIRE_RESPONSE]))
|
||||
await output.register_output(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
await cg.register_component(var, config)
|
||||
yield output.register_output(var, config)
|
||||
yield ble_client.register_ble_node(var, config)
|
||||
yield cg.register_component(var, config)
|
||||
|
@@ -375,19 +375,10 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
||||
|
||||
switch (event) {
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
// Don't reset connection yet - wait for CLOSE_EVT to ensure controller has freed resources
|
||||
// This prevents race condition where we mark slot as free before controller cleanup is complete
|
||||
ESP_LOGD(TAG, "[%d] [%s] Disconnect, reason=0x%02x", this->connection_index_, this->address_str_.c_str(),
|
||||
param->disconnect.reason);
|
||||
// Send disconnection notification but don't free the slot yet
|
||||
this->proxy_->send_device_connection(this->address_, false, 0, param->disconnect.reason);
|
||||
this->reset_connection_(param->disconnect.reason);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_CLOSE_EVT: {
|
||||
ESP_LOGD(TAG, "[%d] [%s] Close, reason=0x%02x, freeing slot", this->connection_index_, this->address_str_.c_str(),
|
||||
param->close.reason);
|
||||
// Now the GATT connection is fully closed and controller resources are freed
|
||||
// Safe to mark the connection slot as available
|
||||
this->reset_connection_(param->close.reason);
|
||||
break;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ namespace esphome::bluetooth_proxy {
|
||||
|
||||
class BluetoothProxy;
|
||||
|
||||
class BluetoothConnection final : public esp32_ble_client::BLEClientBase {
|
||||
class BluetoothConnection : public esp32_ble_client::BLEClientBase {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
|
@@ -183,12 +183,6 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
|
||||
this->send_device_connection(msg.address, false);
|
||||
return;
|
||||
}
|
||||
if (!msg.has_address_type) {
|
||||
ESP_LOGE(TAG, "[%d] [%s] Missing address type in connect request", connection->get_connection_index(),
|
||||
connection->address_str().c_str());
|
||||
this->send_device_connection(msg.address, false);
|
||||
return;
|
||||
}
|
||||
if (connection->state() == espbt::ClientState::CONNECTED ||
|
||||
connection->state() == espbt::ClientState::ESTABLISHED) {
|
||||
this->log_connection_request_ignored_(connection, connection->state());
|
||||
@@ -215,9 +209,13 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
|
||||
connection->set_connection_type(espbt::ConnectionType::V3_WITHOUT_CACHE);
|
||||
this->log_connection_info_(connection, "v3 without cache");
|
||||
}
|
||||
uint64_to_bd_addr(msg.address, connection->remote_bda_);
|
||||
connection->set_remote_addr_type(static_cast<esp_ble_addr_type_t>(msg.address_type));
|
||||
connection->set_state(espbt::ClientState::DISCOVERED);
|
||||
if (msg.has_address_type) {
|
||||
uint64_to_bd_addr(msg.address, connection->remote_bda_);
|
||||
connection->set_remote_addr_type(static_cast<esp_ble_addr_type_t>(msg.address_type));
|
||||
connection->set_state(espbt::ClientState::DISCOVERED);
|
||||
} else {
|
||||
connection->set_state(espbt::ClientState::SEARCHING);
|
||||
}
|
||||
this->send_connections_free();
|
||||
break;
|
||||
}
|
||||
|
@@ -50,7 +50,7 @@ enum BluetoothProxySubscriptionFlag : uint32_t {
|
||||
SUBSCRIPTION_RAW_ADVERTISEMENTS = 1 << 0,
|
||||
};
|
||||
|
||||
class BluetoothProxy final : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
|
||||
class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
|
||||
friend class BluetoothConnection; // Allow connection to update connections_free_response_
|
||||
public:
|
||||
BluetoothProxy();
|
||||
|
@@ -6,19 +6,6 @@ namespace button {
|
||||
|
||||
static const char *const TAG = "button";
|
||||
|
||||
// Function implementation of LOG_BUTTON macro to reduce code size
|
||||
void log_button(const char *tag, const char *prefix, const char *type, Button *obj) {
|
||||
if (obj == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
|
||||
|
||||
if (!obj->get_icon().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Button::press() {
|
||||
ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str());
|
||||
this->press_action();
|
||||
|
@@ -7,10 +7,13 @@
|
||||
namespace esphome {
|
||||
namespace button {
|
||||
|
||||
class Button;
|
||||
void log_button(const char *tag, const char *prefix, const char *type, Button *obj);
|
||||
|
||||
#define LOG_BUTTON(prefix, type, obj) log_button(TAG, prefix, LOG_STR_LITERAL(type), obj)
|
||||
#define LOG_BUTTON(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SUB_BUTTON(name) \
|
||||
protected: \
|
||||
|
@@ -7,83 +7,103 @@ namespace esphome {
|
||||
namespace captive_portal {
|
||||
|
||||
const uint8_t INDEX_GZ[] PROGMEM = {
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x95, 0x56, 0xeb, 0x6f, 0xd4, 0x38, 0x10, 0xff, 0xce,
|
||||
0x5f, 0xe1, 0x33, 0x8f, 0x26, 0xd0, 0x3c, 0xb7, 0xdb, 0x96, 0x6c, 0x12, 0x04, 0xdc, 0x21, 0x90, 0x28, 0x20, 0xb5,
|
||||
0x70, 0x1f, 0x10, 0x52, 0xbd, 0xc9, 0x64, 0x63, 0x9a, 0x38, 0x39, 0xdb, 0xfb, 0x62, 0xb5, 0xf7, 0xb7, 0xdf, 0x38,
|
||||
0xc9, 0x6e, 0xb7, 0x15, 0x9c, 0xee, 0x5a, 0x35, 0x1d, 0xdb, 0xf3, 0xf8, 0xcd, 0x78, 0x1e, 0x8e, 0x7f, 0xcb, 0x9b,
|
||||
0x4c, 0xaf, 0x5b, 0x20, 0xa5, 0xae, 0xab, 0x34, 0x36, 0x5f, 0x52, 0x31, 0x31, 0x4b, 0x40, 0xe0, 0x0a, 0x58, 0x9e,
|
||||
0xc6, 0x35, 0x68, 0x46, 0xb2, 0x92, 0x49, 0x05, 0x3a, 0xf9, 0x7c, 0xf5, 0xc6, 0x39, 0x4f, 0xe3, 0x8a, 0x8b, 0x1b,
|
||||
0x22, 0xa1, 0x4a, 0x78, 0xd6, 0x08, 0x52, 0x4a, 0x28, 0x92, 0x9c, 0x69, 0x16, 0xf1, 0x9a, 0xcd, 0x60, 0x10, 0x11,
|
||||
0xac, 0x86, 0x64, 0xc1, 0x61, 0xd9, 0x36, 0x52, 0x13, 0xe4, 0xd3, 0x20, 0x74, 0x42, 0x97, 0x3c, 0xd7, 0x65, 0x92,
|
||||
0xc3, 0x82, 0x67, 0xe0, 0x74, 0x8b, 0x63, 0x2e, 0xb8, 0xe6, 0xac, 0x72, 0x54, 0xc6, 0x2a, 0x48, 0x82, 0xe3, 0xb9,
|
||||
0x02, 0xd9, 0x2d, 0xd8, 0x14, 0xd7, 0xa2, 0xa1, 0x69, 0xac, 0x32, 0xc9, 0x5b, 0x4d, 0x0c, 0xd4, 0xa4, 0x6e, 0xf2,
|
||||
0x79, 0x05, 0xa9, 0xe7, 0x31, 0x85, 0x90, 0x94, 0xc7, 0x45, 0x0e, 0x2b, 0x77, 0xea, 0x67, 0x99, 0x0f, 0xe7, 0xe7,
|
||||
0xee, 0x77, 0xf5, 0x00, 0x9d, 0x9a, 0xd7, 0x68, 0xcd, 0xad, 0x9a, 0x8c, 0x69, 0xde, 0x08, 0x57, 0x01, 0x93, 0x59,
|
||||
0x99, 0x24, 0x09, 0x7d, 0xa1, 0xd8, 0x02, 0xe8, 0x93, 0x27, 0xd6, 0x9e, 0x69, 0x06, 0xfa, 0x8f, 0x0a, 0x0c, 0xa9,
|
||||
0x5e, 0xad, 0xaf, 0xd8, 0xec, 0x03, 0x02, 0xb7, 0x28, 0x53, 0x3c, 0x07, 0x6a, 0x7f, 0xf5, 0xbf, 0xb9, 0x4a, 0xaf,
|
||||
0x2b, 0x70, 0x73, 0xae, 0xda, 0x8a, 0xad, 0x13, 0x3a, 0x45, 0xad, 0x37, 0xd4, 0x9e, 0x14, 0x73, 0x91, 0x19, 0xe5,
|
||||
0x44, 0x59, 0x60, 0x6f, 0x2a, 0x40, 0x78, 0xc9, 0x05, 0xd3, 0xa5, 0x5b, 0xb3, 0x95, 0xd5, 0x13, 0x5c, 0x58, 0xe1,
|
||||
0x53, 0x0b, 0x9e, 0x05, 0xbe, 0x6f, 0x1f, 0x77, 0x1f, 0xdf, 0xf6, 0xf0, 0xff, 0x44, 0x82, 0x9e, 0x4b, 0x41, 0x98,
|
||||
0x75, 0x1d, 0xb7, 0xc8, 0x49, 0xf2, 0x84, 0x5e, 0x04, 0x21, 0x09, 0x9e, 0xbb, 0xe1, 0xf8, 0xbd, 0x7b, 0x46, 0x4e,
|
||||
0xf0, 0x7f, 0x76, 0xe6, 0x8c, 0x49, 0x70, 0x82, 0x9f, 0x30, 0x74, 0xc7, 0xc4, 0xff, 0x41, 0x49, 0xc1, 0xab, 0x2a,
|
||||
0xa1, 0xa2, 0x11, 0x40, 0x89, 0xd2, 0xb2, 0xb9, 0x81, 0x84, 0x66, 0x73, 0x29, 0x11, 0xfb, 0xeb, 0xa6, 0x6a, 0x24,
|
||||
0xf5, 0xd2, 0x07, 0xff, 0x4b, 0xa1, 0x96, 0x4c, 0xa8, 0xa2, 0x91, 0x75, 0x42, 0xbb, 0xe8, 0x5b, 0x8f, 0x36, 0x7a,
|
||||
0x4b, 0xcc, 0xc7, 0x3e, 0x38, 0x74, 0x1a, 0xc9, 0x67, 0x5c, 0x24, 0xd4, 0x68, 0x3c, 0x47, 0x23, 0xd7, 0xf6, 0x76,
|
||||
0xef, 0x3d, 0x33, 0xde, 0x0f, 0xfe, 0x34, 0xd6, 0xd7, 0xeb, 0x58, 0x2d, 0x66, 0x64, 0x55, 0x57, 0x42, 0x25, 0xb4,
|
||||
0xd4, 0xba, 0x8d, 0x3c, 0x6f, 0xb9, 0x5c, 0xba, 0xcb, 0x91, 0xdb, 0xc8, 0x99, 0x17, 0xfa, 0xbe, 0xef, 0x21, 0x07,
|
||||
0x25, 0x7d, 0x22, 0xd0, 0xf0, 0x84, 0x92, 0x12, 0xf8, 0xac, 0xd4, 0x1d, 0x9d, 0x3e, 0xda, 0xc0, 0x36, 0x36, 0x1c,
|
||||
0xe9, 0xf5, 0xb7, 0x03, 0x2b, 0xfc, 0xc0, 0x0a, 0xbc, 0x60, 0x16, 0xdd, 0xb9, 0x79, 0xd4, 0xb9, 0x79, 0xc6, 0x42,
|
||||
0x12, 0x12, 0xbf, 0xfb, 0x0d, 0x1d, 0x43, 0x0f, 0x2b, 0xe7, 0xde, 0x8a, 0x1c, 0xac, 0x0c, 0x55, 0x9f, 0x3a, 0xcf,
|
||||
0xf7, 0xb2, 0x81, 0xd9, 0x59, 0x04, 0xfe, 0xed, 0x86, 0x11, 0x78, 0x7b, 0x7a, 0xb8, 0x76, 0xc2, 0x2f, 0x87, 0x0c,
|
||||
0xc6, 0x5a, 0x19, 0x7c, 0x39, 0x65, 0x63, 0x32, 0x1e, 0x76, 0xc6, 0x8e, 0xa1, 0xf7, 0x2b, 0x32, 0x5e, 0x20, 0x47,
|
||||
0xed, 0x9c, 0x3a, 0x63, 0x36, 0x22, 0xa3, 0x01, 0x08, 0x52, 0xb8, 0x7d, 0x8a, 0x82, 0x07, 0x7b, 0xce, 0xe8, 0xc7,
|
||||
0x91, 0x97, 0x52, 0x3b, 0xa2, 0xf4, 0xd6, 0xf3, 0xe6, 0xd0, 0x73, 0xf7, 0x7b, 0x83, 0x39, 0x45, 0x29, 0x46, 0x06,
|
||||
0x74, 0x56, 0x5a, 0xd4, 0xc3, 0xc2, 0x2a, 0xf8, 0x0c, 0xb3, 0xbe, 0x11, 0xd4, 0x76, 0x75, 0x09, 0xc2, 0xda, 0x89,
|
||||
0x1a, 0x41, 0xe8, 0x4e, 0xac, 0xfb, 0x27, 0xda, 0xde, 0xec, 0xf3, 0x5f, 0x73, 0x8d, 0x65, 0xa6, 0x5d, 0x53, 0xb0,
|
||||
0xc7, 0xfb, 0xdd, 0x69, 0x93, 0xaf, 0x7f, 0x51, 0x1a, 0x65, 0xd0, 0xd7, 0x05, 0x17, 0x02, 0xe4, 0x15, 0xac, 0xf0,
|
||||
0xe6, 0x2e, 0x5e, 0xbe, 0x26, 0x2f, 0xf3, 0x5c, 0x82, 0x52, 0x11, 0xa1, 0xcf, 0x34, 0xd6, 0x40, 0xf6, 0xdf, 0x75,
|
||||
0x05, 0x77, 0x74, 0xfd, 0xc9, 0xdf, 0x70, 0xf2, 0x01, 0xf4, 0xb2, 0x91, 0x37, 0x83, 0x36, 0x03, 0x6d, 0x62, 0x2a,
|
||||
0x4c, 0x22, 0x4e, 0xd6, 0x2a, 0x57, 0x55, 0xd8, 0x3e, 0xac, 0xc0, 0x46, 0x3b, 0xed, 0xad, 0x57, 0x62, 0x17, 0xa8,
|
||||
0xeb, 0x38, 0xe7, 0x0b, 0x92, 0x55, 0xd8, 0x21, 0xb0, 0x5c, 0x7a, 0x55, 0x94, 0x3c, 0x20, 0xdd, 0x4f, 0x23, 0x32,
|
||||
0x94, 0xbe, 0x49, 0xe8, 0x4f, 0x3a, 0xc0, 0xab, 0xf5, 0xbb, 0xdc, 0x3a, 0x52, 0x58, 0xfb, 0x47, 0xb6, 0xbb, 0x60,
|
||||
0xd5, 0x1c, 0x48, 0x42, 0x74, 0xc9, 0xd5, 0x2d, 0xc0, 0xc9, 0x2f, 0xc5, 0x5a, 0x75, 0x83, 0x52, 0x05, 0x1e, 0x2b,
|
||||
0xcb, 0xa6, 0xe9, 0x60, 0x2e, 0x66, 0x7d, 0x83, 0xa4, 0x0f, 0xe9, 0x3d, 0x44, 0x4e, 0x05, 0x85, 0xde, 0xf3, 0x11,
|
||||
0x2c, 0x3b, 0x65, 0x09, 0x57, 0xa2, 0x75, 0x7b, 0xbb, 0xdf, 0x8c, 0x55, 0xcb, 0xc4, 0x7d, 0x41, 0x03, 0xd0, 0x94,
|
||||
0x0a, 0x36, 0x36, 0xa4, 0x4c, 0xbd, 0x20, 0xd3, 0xde, 0xa0, 0xc7, 0x76, 0xe4, 0xa3, 0x0d, 0x47, 0x8d, 0xa6, 0x5f,
|
||||
0xed, 0x35, 0xc6, 0x1e, 0x86, 0x26, 0xbd, 0xde, 0xda, 0xb7, 0x7e, 0xfc, 0x35, 0x07, 0xb9, 0xbe, 0x84, 0x0a, 0x32,
|
||||
0xdd, 0x48, 0x8b, 0x3e, 0x44, 0x2b, 0x98, 0x4a, 0x9d, 0xc3, 0x6f, 0xaf, 0x2e, 0xde, 0x27, 0x8d, 0x25, 0xed, 0xe3,
|
||||
0x5f, 0x71, 0x9b, 0x51, 0xf0, 0x15, 0x47, 0xc1, 0xdf, 0xc9, 0x91, 0x19, 0x06, 0x47, 0xdf, 0x50, 0xb4, 0xf3, 0xf7,
|
||||
0xfa, 0x76, 0x22, 0x98, 0x72, 0x7e, 0x86, 0x2d, 0xe1, 0xd8, 0x78, 0xe8, 0x9c, 0x8e, 0xed, 0x2d, 0xda, 0x47, 0x04,
|
||||
0x88, 0xbb, 0xeb, 0xeb, 0xd8, 0xdf, 0x4d, 0x8b, 0x4d, 0x9f, 0x6e, 0xa6, 0xcd, 0xca, 0x51, 0xfc, 0x07, 0x17, 0xb3,
|
||||
0x88, 0x8b, 0x12, 0x24, 0xd7, 0x5b, 0x84, 0x8b, 0x13, 0xa2, 0x9d, 0xeb, 0x4d, 0xcb, 0xf2, 0xdc, 0x9c, 0x8c, 0xdb,
|
||||
0xd5, 0xa4, 0xc0, 0x79, 0x62, 0x38, 0x21, 0x0a, 0xa0, 0xde, 0xf6, 0xe7, 0x5d, 0x47, 0x89, 0x9e, 0x8f, 0x1f, 0x6f,
|
||||
0x4d, 0xc2, 0x6d, 0x34, 0x5e, 0x96, 0xc3, 0x2a, 0x3e, 0x13, 0x51, 0x86, 0xc0, 0x41, 0xf6, 0x42, 0x05, 0xab, 0x79,
|
||||
0xb5, 0x8e, 0x14, 0xf6, 0x36, 0x07, 0x07, 0x0d, 0x2f, 0xb6, 0xd3, 0xb9, 0xd6, 0x8d, 0x40, 0xdb, 0x32, 0x07, 0x19,
|
||||
0xf9, 0x93, 0x9e, 0x70, 0x24, 0xcb, 0xf9, 0x5c, 0x45, 0xee, 0x48, 0x42, 0x3d, 0x99, 0xb2, 0xec, 0x66, 0x26, 0x9b,
|
||||
0xb9, 0xc8, 0x9d, 0xcc, 0x74, 0xda, 0xe8, 0x61, 0x50, 0xb0, 0x11, 0x64, 0x93, 0x61, 0x55, 0x14, 0xc5, 0x04, 0x43,
|
||||
0x01, 0x4e, 0xdf, 0xcb, 0xa2, 0xd0, 0x3d, 0x31, 0x62, 0x07, 0x30, 0xdd, 0xd0, 0x6c, 0xf4, 0x18, 0x71, 0x04, 0x3c,
|
||||
0x9e, 0xec, 0xdc, 0xf1, 0x27, 0xd8, 0xc2, 0x15, 0x2a, 0x69, 0xb1, 0xb6, 0x11, 0xe6, 0xb6, 0x66, 0x5c, 0x1c, 0xa2,
|
||||
0x37, 0x69, 0x32, 0x19, 0xc6, 0x0f, 0x86, 0xa5, 0x33, 0xd3, 0x0d, 0xa1, 0x09, 0x0e, 0x98, 0x7e, 0x86, 0x46, 0xe1,
|
||||
0xa9, 0xdf, 0xae, 0xb6, 0xee, 0x90, 0x20, 0x9b, 0x1d, 0x77, 0x51, 0xc1, 0x6a, 0xf2, 0x7d, 0xae, 0x34, 0x2f, 0xd6,
|
||||
0xce, 0x30, 0x83, 0x23, 0x4c, 0x16, 0x9c, 0xbd, 0x53, 0x64, 0x05, 0x10, 0x93, 0xce, 0x86, 0xc3, 0x35, 0xd4, 0x6a,
|
||||
0x88, 0xd3, 0x5e, 0x4d, 0x97, 0xa0, 0x77, 0x75, 0xfd, 0x1b, 0xb7, 0xc9, 0xc5, 0x4d, 0xcd, 0x24, 0x8e, 0x0a, 0x67,
|
||||
0xda, 0x60, 0x4c, 0xeb, 0xc8, 0x39, 0xc3, 0xbb, 0x1a, 0xb6, 0x8c, 0x32, 0xf4, 0x1c, 0x61, 0x76, 0xb3, 0x75, 0x17,
|
||||
0xef, 0xa0, 0x5d, 0x11, 0xd5, 0x54, 0x3c, 0x1f, 0xf8, 0x3a, 0x16, 0xe2, 0xef, 0xc3, 0x13, 0xe0, 0x75, 0x13, 0xb3,
|
||||
0xb7, 0x0b, 0xf5, 0x49, 0x71, 0xce, 0x02, 0xff, 0x27, 0x37, 0x92, 0x17, 0x45, 0x38, 0x2d, 0xf6, 0x91, 0x32, 0x63,
|
||||
0xd2, 0x94, 0x46, 0x97, 0x5a, 0xb1, 0xd7, 0xbf, 0x66, 0x4c, 0x66, 0xe0, 0x03, 0x05, 0x23, 0x8c, 0xef, 0x9b, 0x80,
|
||||
0xf0, 0x3c, 0xc1, 0x4e, 0x95, 0x1e, 0xb4, 0x2f, 0x64, 0x0c, 0x76, 0x47, 0x48, 0xdd, 0x69, 0x46, 0xfd, 0x59, 0x87,
|
||||
0x3e, 0x7d, 0xdd, 0x60, 0x7d, 0x60, 0xdb, 0x11, 0x33, 0xa2, 0x1b, 0x32, 0x84, 0xc0, 0x75, 0xdd, 0x78, 0x2a, 0xd3,
|
||||
0x4f, 0x15, 0x30, 0x05, 0x64, 0xc9, 0xb8, 0x76, 0xb1, 0x1a, 0x3b, 0xfe, 0xbe, 0x8e, 0x51, 0x29, 0xb2, 0xa6, 0x43,
|
||||
0xc1, 0xc6, 0xe5, 0xa8, 0x37, 0x70, 0x09, 0xda, 0x68, 0x32, 0x06, 0x46, 0x69, 0x6c, 0x46, 0x2e, 0x61, 0x5d, 0x4b,
|
||||
0x4b, 0xbc, 0x25, 0x2f, 0xb8, 0x79, 0xb2, 0xa4, 0x71, 0x97, 0xe4, 0x46, 0x83, 0x89, 0x73, 0xff, 0xbc, 0xea, 0xa8,
|
||||
0x0a, 0xc4, 0x0c, 0x27, 0xe9, 0x28, 0x24, 0xe8, 0x76, 0x06, 0x65, 0x53, 0x61, 0x58, 0x93, 0xcb, 0xcb, 0x77, 0xbf,
|
||||
0xa7, 0x06, 0xcc, 0xad, 0x1c, 0xf6, 0xa7, 0x5e, 0xcc, 0x10, 0x83, 0xd4, 0xe9, 0x49, 0xff, 0xa8, 0x6a, 0xb1, 0xbf,
|
||||
0xa0, 0x07, 0xf9, 0x1d, 0x1d, 0x9f, 0x86, 0xcd, 0x5e, 0x4f, 0xf7, 0xd7, 0x95, 0x4a, 0x7a, 0x89, 0x80, 0x62, 0x6f,
|
||||
0x58, 0xc4, 0x9e, 0x01, 0xdc, 0x9f, 0x97, 0x03, 0x1f, 0xc6, 0xe9, 0xe3, 0xd5, 0x4b, 0xf2, 0xb9, 0xc5, 0x26, 0x00,
|
||||
0x7d, 0xd8, 0x3a, 0xaf, 0xf0, 0x65, 0x58, 0x36, 0x79, 0xf2, 0xe9, 0xe3, 0xe5, 0xd5, 0xde, 0xc3, 0x79, 0xc7, 0x44,
|
||||
0x40, 0x64, 0xfd, 0xf3, 0x6e, 0x5e, 0x69, 0xde, 0x32, 0xa9, 0x3b, 0xb5, 0x8e, 0xe9, 0x22, 0x3b, 0x1f, 0xba, 0x73,
|
||||
0x7c, 0x03, 0x41, 0xef, 0x46, 0x2f, 0x98, 0x92, 0x1d, 0xaa, 0x9d, 0xb5, 0x7b, 0xb8, 0xbc, 0xfe, 0xb6, 0xbd, 0xfe,
|
||||
0xea, 0xbd, 0xee, 0xa5, 0xfb, 0x0f, 0x53, 0x58, 0x46, 0xb2, 0xf9, 0x0a, 0x00, 0x00};
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x6d, 0x6f, 0xdb, 0x38, 0x12, 0xfe, 0xde,
|
||||
0x5f, 0x31, 0xa7, 0x36, 0x6b, 0x6b, 0x1b, 0x51, 0x22, 0xe5, 0xb7, 0xd8, 0x92, 0x16, 0x69, 0xae, 0x8b, 0x5d, 0xa0,
|
||||
0xdd, 0x2d, 0x90, 0x6c, 0xef, 0x43, 0x51, 0x20, 0xb4, 0x34, 0xb2, 0xd8, 0x48, 0xa4, 0x4e, 0xa4, 0x5f, 0x52, 0xc3,
|
||||
0xf7, 0xdb, 0x0f, 0x94, 0x6c, 0xc7, 0xe9, 0x35, 0x87, 0xeb, 0xe2, 0x0e, 0x87, 0xdd, 0x18, 0x21, 0x86, 0xe4, 0xcc,
|
||||
0x70, 0xe6, 0xf1, 0x0c, 0x67, 0xcc, 0xe8, 0x2f, 0x99, 0x4a, 0xcd, 0x7d, 0x8d, 0x50, 0x98, 0xaa, 0x4c, 0x22, 0x3b,
|
||||
0x42, 0xc9, 0xe5, 0x22, 0x46, 0x99, 0x44, 0x05, 0xf2, 0x2c, 0x89, 0x2a, 0x34, 0x1c, 0xd2, 0x82, 0x37, 0x1a, 0x4d,
|
||||
0xfc, 0xdb, 0xcd, 0x8f, 0xde, 0x04, 0xfc, 0x24, 0x2a, 0x85, 0xbc, 0x83, 0x06, 0xcb, 0x58, 0xa4, 0x4a, 0x42, 0xd1,
|
||||
0x60, 0x1e, 0x67, 0xdc, 0xf0, 0xa9, 0xa8, 0xf8, 0x02, 0x2d, 0x43, 0x2b, 0x26, 0x79, 0x85, 0xf1, 0x4a, 0xe0, 0xba,
|
||||
0x56, 0x8d, 0x81, 0x54, 0x49, 0x83, 0xd2, 0xc4, 0xce, 0x5a, 0x64, 0xa6, 0x88, 0x33, 0x5c, 0x89, 0x14, 0xbd, 0x76,
|
||||
0x72, 0x2e, 0xa4, 0x30, 0x82, 0x97, 0x9e, 0x4e, 0x79, 0x89, 0x31, 0x3d, 0x5f, 0x6a, 0x6c, 0xda, 0x09, 0x9f, 0x97,
|
||||
0x18, 0x4b, 0xe5, 0xf8, 0x49, 0xa4, 0xd3, 0x46, 0xd4, 0x06, 0xac, 0xbd, 0x71, 0xa5, 0xb2, 0x65, 0x89, 0x89, 0xef,
|
||||
0x73, 0xad, 0xd1, 0x68, 0x5f, 0xc8, 0x0c, 0x37, 0x64, 0x14, 0x86, 0x29, 0xe3, 0xe3, 0x9c, 0x7c, 0xd2, 0xcf, 0x32,
|
||||
0x95, 0x2e, 0x2b, 0x94, 0x86, 0x94, 0x2a, 0xe5, 0x46, 0x28, 0x49, 0x34, 0xf2, 0x26, 0x2d, 0xe2, 0x38, 0x76, 0x7e,
|
||||
0xd0, 0x7c, 0x85, 0xce, 0x77, 0xdf, 0xf5, 0x8f, 0x4c, 0x0b, 0x34, 0xaf, 0x4b, 0xb4, 0xa4, 0x7e, 0x75, 0x7f, 0xc3,
|
||||
0x17, 0xbf, 0xf0, 0x0a, 0xfb, 0x0e, 0xd7, 0x22, 0x43, 0xc7, 0xfd, 0x10, 0x7c, 0x24, 0xda, 0xdc, 0x97, 0x48, 0x32,
|
||||
0xa1, 0xeb, 0x92, 0xdf, 0xc7, 0xce, 0xbc, 0x54, 0xe9, 0x9d, 0xe3, 0xce, 0xf2, 0xa5, 0x4c, 0xad, 0x72, 0xd0, 0x7d,
|
||||
0x74, 0xb7, 0x25, 0x1a, 0x30, 0xf1, 0x5b, 0x6e, 0x0a, 0x52, 0xf1, 0x4d, 0xbf, 0x23, 0x84, 0xec, 0xb3, 0xef, 0xfb,
|
||||
0xf8, 0x92, 0x06, 0x81, 0x7b, 0xde, 0x0e, 0x81, 0xeb, 0xd3, 0x20, 0x98, 0x35, 0x68, 0x96, 0x8d, 0x04, 0xde, 0xbf,
|
||||
0x8d, 0x6a, 0x6e, 0x0a, 0xc8, 0x62, 0xa7, 0xa2, 0x8c, 0x04, 0xc1, 0x04, 0xe8, 0x05, 0x61, 0x43, 0x8f, 0x52, 0x12,
|
||||
0x7a, 0x74, 0x98, 0x8e, 0xbd, 0x21, 0xd0, 0x81, 0x37, 0x04, 0xc6, 0xc8, 0x10, 0x82, 0xcf, 0x0e, 0xe4, 0xa2, 0x2c,
|
||||
0x63, 0x47, 0x2a, 0x89, 0x0e, 0x68, 0xd3, 0xa8, 0x3b, 0x8c, 0x9d, 0x74, 0xd9, 0x34, 0x28, 0xcd, 0x95, 0x2a, 0x55,
|
||||
0xe3, 0xf8, 0xc9, 0x33, 0x78, 0xf4, 0xf7, 0xcd, 0x47, 0x98, 0x86, 0x4b, 0x9d, 0xab, 0xa6, 0x8a, 0x9d, 0xf6, 0x4b,
|
||||
0xe9, 0xbf, 0xd8, 0x9a, 0x1d, 0xd8, 0xc1, 0x3d, 0xd9, 0xf4, 0x54, 0x23, 0x16, 0x42, 0xc6, 0x0e, 0x65, 0x40, 0x27,
|
||||
0x8e, 0x9f, 0xdc, 0xba, 0xbb, 0x23, 0x26, 0xdc, 0x62, 0xb2, 0xf7, 0x52, 0xf5, 0x3f, 0xdc, 0x46, 0x7a, 0xb5, 0x80,
|
||||
0x4d, 0x55, 0x4a, 0x1d, 0x3b, 0x85, 0x31, 0xf5, 0xd4, 0xf7, 0xd7, 0xeb, 0x35, 0x59, 0x87, 0x44, 0x35, 0x0b, 0x9f,
|
||||
0x05, 0x41, 0xe0, 0xeb, 0xd5, 0xc2, 0x81, 0x2e, 0x3e, 0x1c, 0x36, 0x70, 0xa0, 0x40, 0xb1, 0x28, 0x4c, 0x4b, 0x27,
|
||||
0x2f, 0xb6, 0xb8, 0x8b, 0x2c, 0x47, 0x72, 0xfb, 0xf1, 0xe4, 0x14, 0x71, 0x72, 0x0a, 0xfe, 0x70, 0x82, 0x66, 0xef,
|
||||
0xad, 0x35, 0x6a, 0xcc, 0x19, 0x30, 0x08, 0xda, 0x0f, 0xf3, 0x2c, 0xbd, 0x9f, 0x79, 0x5f, 0xcc, 0xe0, 0x64, 0x06,
|
||||
0x0c, 0x9e, 0x01, 0xb0, 0x6a, 0xe4, 0x5d, 0x1c, 0xc5, 0xa9, 0xdd, 0x5e, 0xd1, 0xe0, 0x61, 0xc1, 0xca, 0xfc, 0x34,
|
||||
0x3a, 0x9d, 0x7b, 0xec, 0xbd, 0x65, 0xb0, 0xd8, 0x1f, 0x85, 0x3c, 0x56, 0xd0, 0xf7, 0x23, 0x3e, 0x84, 0xe1, 0x7e,
|
||||
0x65, 0xe8, 0x59, 0xfa, 0x38, 0xb3, 0x27, 0xc1, 0x70, 0xc5, 0x0a, 0x5a, 0x79, 0x23, 0x6f, 0xc8, 0x43, 0x08, 0xf7,
|
||||
0x26, 0x85, 0x10, 0xae, 0x58, 0x31, 0x7a, 0x3f, 0x3a, 0x5d, 0xf3, 0xc2, 0xcf, 0x3d, 0x0b, 0xf3, 0xd4, 0x71, 0x1e,
|
||||
0x30, 0x50, 0xa7, 0x18, 0x90, 0x4f, 0x4a, 0xc8, 0xbe, 0xe3, 0xb8, 0xbb, 0x1c, 0x4d, 0x5a, 0xf4, 0x1d, 0x3f, 0x55,
|
||||
0x32, 0x17, 0x0b, 0xf2, 0x49, 0x2b, 0xe9, 0xb8, 0xc4, 0x14, 0x28, 0xfb, 0x07, 0x51, 0x2b, 0x88, 0xed, 0x4e, 0xff,
|
||||
0xcb, 0x1d, 0xe3, 0x6e, 0x8f, 0xf9, 0x61, 0x84, 0x29, 0x31, 0x36, 0xc4, 0x66, 0xf4, 0xf9, 0x71, 0x75, 0xae, 0xb2,
|
||||
0xfb, 0x27, 0x52, 0xa7, 0xa0, 0x5d, 0xde, 0x08, 0x29, 0xb1, 0xb9, 0xc1, 0x8d, 0x89, 0x9d, 0xb7, 0x97, 0x57, 0x70,
|
||||
0x99, 0x65, 0x0d, 0x6a, 0x3d, 0x05, 0xe7, 0xa5, 0x21, 0x15, 0x4f, 0xff, 0x73, 0x5d, 0xf4, 0x91, 0xae, 0xbf, 0x89,
|
||||
0x1f, 0x05, 0xfc, 0x82, 0x66, 0xad, 0x9a, 0xbb, 0xbd, 0x36, 0x6b, 0xda, 0xcc, 0x66, 0x60, 0x13, 0x1b, 0xc2, 0x6b,
|
||||
0x4d, 0x74, 0x29, 0x52, 0xec, 0x53, 0x97, 0x54, 0xbc, 0x7e, 0xf0, 0x4a, 0x1e, 0x80, 0xba, 0x8d, 0x32, 0xb1, 0x82,
|
||||
0xb4, 0xe4, 0x5a, 0xc7, 0x8e, 0xec, 0x54, 0x39, 0xb0, 0x4f, 0x1b, 0x25, 0xd3, 0x52, 0xa4, 0x77, 0xb1, 0xf3, 0x95,
|
||||
0x1b, 0xe2, 0xd5, 0xfd, 0xcf, 0x59, 0xbf, 0xa7, 0xb5, 0xc8, 0x7a, 0x2e, 0x59, 0xf1, 0x72, 0x89, 0x10, 0x83, 0x29,
|
||||
0x84, 0x7e, 0x30, 0x70, 0xf6, 0xa4, 0x58, 0xad, 0xef, 0x7a, 0x2e, 0xc9, 0x55, 0xba, 0xd4, 0x7d, 0xd7, 0x39, 0x64,
|
||||
0x69, 0xc4, 0xbb, 0x3b, 0xd4, 0x79, 0xee, 0x7c, 0x61, 0x91, 0x57, 0x62, 0x6e, 0x9c, 0x87, 0x6c, 0x7e, 0xb1, 0xd5,
|
||||
0x7d, 0x49, 0x1a, 0xad, 0x85, 0xbb, 0x3b, 0x2e, 0x46, 0xba, 0xe6, 0xf2, 0x4b, 0x41, 0x6b, 0xa0, 0x4d, 0x1a, 0x49,
|
||||
0x2c, 0x65, 0x33, 0xa7, 0xe6, 0xf2, 0x78, 0xa0, 0xcf, 0x0f, 0xe4, 0x8b, 0xad, 0xe8, 0x4b, 0x7b, 0x4b, 0xde, 0x1d,
|
||||
0x35, 0x46, 0x7e, 0x26, 0x56, 0xc9, 0xed, 0xce, 0x7d, 0xf0, 0xe3, 0xef, 0x4b, 0x6c, 0xee, 0xaf, 0xb1, 0xc4, 0xd4,
|
||||
0xa8, 0xa6, 0xef, 0x3c, 0x97, 0x68, 0x1c, 0xb7, 0x73, 0xf8, 0xa7, 0x9b, 0xb7, 0x6f, 0x62, 0xd5, 0x6f, 0xdc, 0xf3,
|
||||
0xa7, 0xb8, 0x6d, 0xb5, 0xf8, 0xd0, 0x60, 0xf9, 0x8f, 0xb8, 0x67, 0xeb, 0x45, 0xef, 0xa3, 0xe3, 0x92, 0xd6, 0xdf,
|
||||
0xdb, 0x87, 0xa2, 0x61, 0x13, 0xfb, 0xe5, 0xa6, 0x2a, 0xcf, 0xad, 0x87, 0xde, 0x68, 0xe8, 0xee, 0x6e, 0x77, 0xee,
|
||||
0xce, 0x9d, 0x45, 0x7e, 0x77, 0xef, 0x27, 0x51, 0x7b, 0x05, 0x27, 0xdf, 0x6f, 0xe7, 0x6a, 0xe3, 0x69, 0xf1, 0x59,
|
||||
0xc8, 0xc5, 0x54, 0xc8, 0x02, 0x1b, 0x61, 0x76, 0x99, 0x58, 0x9d, 0x0b, 0x59, 0x2f, 0xcd, 0xb6, 0xe6, 0x59, 0x66,
|
||||
0x77, 0x86, 0xf5, 0x66, 0x96, 0x2b, 0x69, 0x2c, 0x27, 0x4e, 0x29, 0x56, 0xbb, 0x6e, 0xbf, 0xbd, 0x5b, 0xa6, 0x17,
|
||||
0xc3, 0xb3, 0x9d, 0x0d, 0xb8, 0xad, 0xc1, 0x8d, 0xf1, 0x78, 0x29, 0x16, 0x72, 0x9a, 0xa2, 0x34, 0xd8, 0x74, 0x42,
|
||||
0x39, 0xaf, 0x44, 0x79, 0x3f, 0xd5, 0x5c, 0x6a, 0x4f, 0x63, 0x23, 0xf2, 0xdd, 0x7c, 0x69, 0x8c, 0x92, 0xdb, 0xb9,
|
||||
0x6a, 0x32, 0x6c, 0xa6, 0xc1, 0xac, 0x23, 0xbc, 0x86, 0x67, 0x62, 0xa9, 0xa7, 0x24, 0x6c, 0xb0, 0x9a, 0xcd, 0x79,
|
||||
0x7a, 0xb7, 0x68, 0xd4, 0x52, 0x66, 0x5e, 0x6a, 0x6f, 0xe1, 0xe9, 0x73, 0x9a, 0xf3, 0x10, 0xd3, 0xd9, 0x7e, 0x96,
|
||||
0xe7, 0xf9, 0xac, 0x14, 0x12, 0xbd, 0xee, 0x56, 0x9b, 0x32, 0x32, 0xb0, 0x62, 0x27, 0x66, 0x12, 0x66, 0x17, 0x3a,
|
||||
0x1b, 0x69, 0x10, 0x9c, 0xcd, 0x0e, 0xee, 0x04, 0xb3, 0x74, 0xd9, 0x68, 0xd5, 0x4c, 0x6b, 0x25, 0xac, 0x99, 0xbb,
|
||||
0x8a, 0x0b, 0x79, 0x6a, 0xbd, 0x0d, 0x93, 0xd9, 0xbe, 0x3c, 0x4d, 0x85, 0x6c, 0x8f, 0x69, 0x8b, 0xd4, 0xac, 0x12,
|
||||
0xb2, 0x2b, 0xb2, 0x53, 0x36, 0x0a, 0xea, 0xcd, 0x8e, 0xec, 0x03, 0x64, 0x7b, 0xe0, 0xce, 0x4b, 0xdc, 0xcc, 0x3e,
|
||||
0x2d, 0xb5, 0x11, 0xf9, 0xbd, 0xb7, 0x2f, 0xd2, 0x53, 0x5d, 0xf3, 0x14, 0xbd, 0x39, 0x9a, 0x35, 0xa2, 0x9c, 0xb5,
|
||||
0x67, 0x78, 0xc2, 0x60, 0xa5, 0xf7, 0x38, 0x1d, 0xd5, 0xb4, 0x01, 0xfa, 0x58, 0xd7, 0xbf, 0xe3, 0xb6, 0xb1, 0xb8,
|
||||
0xad, 0x78, 0xb3, 0x10, 0xd2, 0x9b, 0x2b, 0x63, 0x54, 0x35, 0xf5, 0xc6, 0xf5, 0x66, 0xb6, 0x5f, 0xb2, 0xca, 0xa6,
|
||||
0xd4, 0x9a, 0xd9, 0xd6, 0xde, 0x03, 0xde, 0xb4, 0xde, 0x80, 0x56, 0xa5, 0xc8, 0xf6, 0x7c, 0x2d, 0x0b, 0x04, 0x47,
|
||||
0x78, 0xe8, 0xb0, 0xde, 0x80, 0x5d, 0x3b, 0x40, 0x3d, 0xc8, 0x27, 0x9c, 0x06, 0x5f, 0xf9, 0x46, 0xb2, 0x3c, 0x67,
|
||||
0xf3, 0xfc, 0x88, 0x94, 0x2d, 0xa1, 0x3b, 0xb1, 0x8f, 0x0a, 0x36, 0xa8, 0x37, 0xb3, 0xc3, 0x77, 0x33, 0xa8, 0x37,
|
||||
0x3b, 0xd1, 0xa6, 0xc5, 0xf6, 0x44, 0x4b, 0x1b, 0xaa, 0xd3, 0x65, 0x53, 0xf6, 0x9d, 0xaf, 0x84, 0xee, 0x59, 0x78,
|
||||
0xf5, 0x50, 0xe2, 0x7a, 0x4f, 0x97, 0xb8, 0x1e, 0xd8, 0xa6, 0xe8, 0x95, 0xda, 0xc4, 0xbd, 0xb6, 0xd8, 0x0c, 0x80,
|
||||
0x0d, 0x7a, 0x67, 0xe1, 0xeb, 0xb3, 0xf0, 0xea, 0xbf, 0x52, 0xbb, 0x7e, 0x77, 0xe1, 0xfa, 0x86, 0xaa, 0xf5, 0x8d,
|
||||
0x15, 0xab, 0xf3, 0xce, 0x3a, 0x7f, 0x16, 0xbe, 0x76, 0xdc, 0x9d, 0x20, 0x5a, 0x2c, 0xe8, 0xff, 0x02, 0xda, 0x7f,
|
||||
0xc5, 0x31, 0xbc, 0xa4, 0x13, 0x72, 0x01, 0xed, 0xd0, 0x41, 0x44, 0xc2, 0x09, 0x8c, 0xaf, 0x06, 0x64, 0x40, 0xc1,
|
||||
0xb6, 0x43, 0x23, 0x18, 0x93, 0xc9, 0x05, 0xd0, 0x11, 0x09, 0xc7, 0x40, 0x19, 0x30, 0x4a, 0x86, 0x6f, 0x58, 0x48,
|
||||
0x46, 0x43, 0x18, 0x5f, 0xb1, 0x80, 0x84, 0x0c, 0x3a, 0xde, 0x11, 0x61, 0x0c, 0x42, 0xcb, 0x12, 0x56, 0x01, 0xb0,
|
||||
0x34, 0x24, 0xc1, 0x18, 0x02, 0x18, 0x91, 0xe0, 0x82, 0x4c, 0x46, 0x30, 0x21, 0x63, 0x0a, 0x8c, 0x0c, 0x86, 0xa5,
|
||||
0x37, 0x24, 0x14, 0x46, 0x24, 0x1c, 0xf1, 0x09, 0x19, 0x84, 0xd0, 0x0e, 0x1d, 0x1c, 0x63, 0xc2, 0x98, 0x47, 0x02,
|
||||
0xfa, 0x26, 0x24, 0x6c, 0x0c, 0x63, 0x32, 0x18, 0x5c, 0xd2, 0x11, 0xb9, 0x18, 0x40, 0x37, 0x76, 0xf0, 0x52, 0x06,
|
||||
0xc3, 0xa7, 0x40, 0x63, 0x7f, 0x5e, 0xd0, 0x42, 0xc2, 0x28, 0x84, 0xe4, 0x62, 0xc2, 0x6d, 0x5f, 0xca, 0xa0, 0x1b,
|
||||
0x3b, 0xdc, 0x28, 0x85, 0xe0, 0x77, 0x63, 0x16, 0xfe, 0x79, 0x31, 0xa3, 0x16, 0x01, 0x46, 0x06, 0xe1, 0x25, 0x0d,
|
||||
0xc9, 0x08, 0xda, 0xa1, 0x3b, 0x9b, 0x32, 0x98, 0x5c, 0x5d, 0xc0, 0x04, 0x46, 0x64, 0x34, 0x81, 0x0b, 0x18, 0x5a,
|
||||
0x74, 0x2f, 0xc8, 0x64, 0xd0, 0x09, 0x79, 0x8c, 0x7c, 0x2b, 0x8c, 0x83, 0x3f, 0x30, 0x8c, 0x4f, 0xf9, 0xf4, 0x07,
|
||||
0x76, 0xe9, 0xff, 0x71, 0x05, 0x45, 0x7e, 0xd7, 0x86, 0x45, 0x7e, 0xf7, 0x3c, 0x60, 0xbb, 0xa8, 0x24, 0xb2, 0xdd,
|
||||
0x48, 0x12, 0x15, 0x14, 0x44, 0x16, 0x57, 0x3c, 0x4d, 0x4e, 0x5a, 0xfd, 0xc8, 0x2f, 0xe8, 0x61, 0xab, 0xa0, 0xc9,
|
||||
0xa3, 0xc6, 0xbd, 0xdb, 0x6b, 0x2b, 0x7d, 0x72, 0x53, 0x20, 0xbc, 0xbe, 0x7e, 0x07, 0x6b, 0x51, 0x96, 0x20, 0xd5,
|
||||
0x1a, 0x4c, 0x73, 0x0f, 0x46, 0xd9, 0x57, 0x03, 0x89, 0xa9, 0xb1, 0xa4, 0x29, 0x10, 0xf6, 0x7d, 0x04, 0x21, 0x24,
|
||||
0x9a, 0x37, 0xc9, 0xbb, 0x12, 0xb9, 0x46, 0x58, 0x88, 0x15, 0x82, 0x30, 0xa0, 0x55, 0x85, 0x60, 0x84, 0x1d, 0x8e,
|
||||
0x82, 0x2d, 0x5f, 0xe4, 0x77, 0x87, 0x74, 0x8d, 0xb2, 0xc8, 0x62, 0x89, 0x26, 0xd9, 0x77, 0xc4, 0x51, 0x11, 0x76,
|
||||
0x56, 0x5d, 0xa3, 0x31, 0x42, 0x2e, 0xac, 0x55, 0x61, 0x12, 0xd9, 0x5f, 0xb7, 0xc0, 0xdb, 0xdf, 0x0c, 0xb1, 0xbf,
|
||||
0x16, 0xb9, 0xb0, 0x6f, 0x06, 0x49, 0xd4, 0x76, 0x91, 0x56, 0x83, 0x6d, 0x64, 0xba, 0x07, 0x8e, 0x96, 0x2a, 0x51,
|
||||
0x2e, 0x4c, 0x11, 0x87, 0x0c, 0xea, 0x92, 0xa7, 0x58, 0xa8, 0x32, 0xc3, 0x26, 0xbe, 0xbe, 0xfe, 0xf9, 0xaf, 0xf6,
|
||||
0x35, 0xc4, 0x9a, 0x70, 0x94, 0xac, 0xf5, 0x5d, 0x27, 0x68, 0x89, 0xbd, 0xdc, 0x68, 0xd0, 0xbd, 0x6b, 0xd4, 0x5c,
|
||||
0xeb, 0xb5, 0x6a, 0xb2, 0x47, 0x5a, 0xde, 0x1d, 0x16, 0xf7, 0x9a, 0xda, 0xff, 0xb6, 0x1f, 0xed, 0x84, 0xf4, 0x72,
|
||||
0x5e, 0x09, 0x93, 0x5c, 0xf3, 0x15, 0x46, 0x7e, 0xb7, 0x91, 0x44, 0xbe, 0x75, 0xa0, 0xe3, 0x2d, 0xf6, 0x32, 0x05,
|
||||
0x4d, 0x7e, 0xbd, 0xb9, 0x84, 0xdf, 0xea, 0x8c, 0x1b, 0xec, 0xb0, 0x6f, 0xbd, 0xac, 0xd0, 0x14, 0x2a, 0x8b, 0xdf,
|
||||
0xfd, 0x7a, 0x7d, 0x73, 0xf4, 0x78, 0xd9, 0x32, 0x01, 0xca, 0xb4, 0x7b, 0x6f, 0x59, 0x96, 0x46, 0xd4, 0xbc, 0x31,
|
||||
0xad, 0x5a, 0xcf, 0x66, 0xc7, 0xc1, 0xa3, 0x76, 0x3f, 0x17, 0x25, 0x76, 0x4e, 0xed, 0x05, 0xfd, 0x04, 0xbe, 0x66,
|
||||
0xe3, 0xe1, 0xec, 0x2f, 0xac, 0xf4, 0xbb, 0x00, 0xf2, 0xbb, 0x68, 0xf2, 0xdb, 0xd7, 0xa8, 0x7f, 0x02, 0x14, 0xee,
|
||||
0xbc, 0x64, 0x9d, 0x12, 0x00, 0x00};
|
||||
|
||||
} // namespace captive_portal
|
||||
} // namespace esphome
|
||||
|
@@ -153,8 +153,8 @@ void CCS811Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "CCS811");
|
||||
LOG_I2C_DEVICE(this)
|
||||
LOG_UPDATE_INTERVAL(this)
|
||||
LOG_SENSOR(" ", "CO2 Sensor", this->co2_);
|
||||
LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_);
|
||||
LOG_SENSOR(" ", "CO2 Sensor", this->co2_)
|
||||
LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_)
|
||||
LOG_TEXT_SENSOR(" ", "Firmware Version Sensor", this->version_)
|
||||
if (this->baseline_) {
|
||||
ESP_LOGCONFIG(TAG, " Baseline: %04X", *this->baseline_);
|
||||
|
@@ -327,7 +327,7 @@ void Climate::add_on_control_callback(std::function<void(ClimateCall &)> &&callb
|
||||
static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL;
|
||||
|
||||
optional<ClimateDeviceRestoreState> Climate::restore_state_() {
|
||||
this->rtc_ = global_preferences->make_preference<ClimateDeviceRestoreState>(this->get_preference_hash() ^
|
||||
this->rtc_ = global_preferences->make_preference<ClimateDeviceRestoreState>(this->get_object_id_hash() ^
|
||||
RESTORE_STATE_VERSION);
|
||||
ClimateDeviceRestoreState recovered{};
|
||||
if (!this->rtc_.load(&recovered))
|
||||
|
@@ -228,9 +228,9 @@ async def cover_stop_to_code(config, action_id, template_arg, args):
|
||||
|
||||
|
||||
@automation.register_action("cover.toggle", ToggleAction, COVER_ACTION_SCHEMA)
|
||||
async def cover_toggle_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
def cover_toggle_to_code(config, action_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
yield cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
|
||||
|
@@ -194,7 +194,7 @@ void Cover::publish_state(bool save) {
|
||||
}
|
||||
}
|
||||
optional<CoverRestoreState> Cover::restore_state_() {
|
||||
this->rtc_ = global_preferences->make_preference<CoverRestoreState>(this->get_preference_hash());
|
||||
this->rtc_ = global_preferences->make_preference<CoverRestoreState>(this->get_object_id_hash());
|
||||
CoverRestoreState recovered{};
|
||||
if (!this->rtc_.load(&recovered))
|
||||
return {};
|
||||
|
@@ -41,7 +41,7 @@ void DutyTimeSensor::setup() {
|
||||
uint32_t seconds = 0;
|
||||
|
||||
if (this->restore_) {
|
||||
this->pref_ = global_preferences->make_preference<uint32_t>(this->get_preference_hash());
|
||||
this->pref_ = global_preferences->make_preference<uint32_t>(this->get_object_id_hash());
|
||||
this->pref_.load(&seconds);
|
||||
}
|
||||
|
||||
|
@@ -824,9 +824,8 @@ async def to_code(config):
|
||||
cg.set_cpp_standard("gnu++20")
|
||||
cg.add_build_flag("-DUSE_ESP32")
|
||||
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
||||
variant = config[CONF_VARIANT]
|
||||
cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{variant}")
|
||||
cg.add_define("ESPHOME_VARIANT", VARIANT_FRIENDLY[variant])
|
||||
cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}")
|
||||
cg.add_define("ESPHOME_VARIANT", VARIANT_FRIENDLY[config[CONF_VARIANT]])
|
||||
cg.add_define(ThreadModel.MULTI_ATOMICS)
|
||||
|
||||
cg.add_platformio_option("lib_ldf_mode", "off")
|
||||
@@ -860,7 +859,6 @@ async def to_code(config):
|
||||
cg.add_platformio_option(
|
||||
"platform_packages", ["espressif/toolchain-esp32ulp@2.35.0-20220830"]
|
||||
)
|
||||
add_idf_sdkconfig_option(f"CONFIG_IDF_TARGET_{variant}", True)
|
||||
add_idf_sdkconfig_option(
|
||||
f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True
|
||||
)
|
||||
|
@@ -306,7 +306,7 @@ void ESP32BLE::loop() {
|
||||
case BLEEvent::GATTS: {
|
||||
esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event;
|
||||
esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if;
|
||||
esp_ble_gatts_cb_param_t *param = &ble_event->event_.gatts.gatts_param;
|
||||
esp_ble_gatts_cb_param_t *param = ble_event->event_.gatts.gatts_param;
|
||||
ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
|
||||
for (auto *gatts_handler : this->gatts_event_handlers_) {
|
||||
gatts_handler->gatts_event_handler(event, gatts_if, param);
|
||||
@@ -316,7 +316,7 @@ void ESP32BLE::loop() {
|
||||
case BLEEvent::GATTC: {
|
||||
esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event;
|
||||
esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if;
|
||||
esp_ble_gattc_cb_param_t *param = &ble_event->event_.gattc.gattc_param;
|
||||
esp_ble_gattc_cb_param_t *param = ble_event->event_.gattc.gattc_param;
|
||||
ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
|
||||
for (auto *gattc_handler : this->gattc_event_handlers_) {
|
||||
gattc_handler->gattc_event_handler(event, gattc_if, param);
|
||||
|
@@ -61,24 +61,10 @@ static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.rssi) == sizeof(es
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.remote_addr) == sizeof(esp_bt_status_t) + sizeof(int8_t),
|
||||
"remote_addr must follow rssi in read_rssi_cmpl");
|
||||
|
||||
// Param struct sizes on ESP32
|
||||
static constexpr size_t GATTC_PARAM_SIZE = 28;
|
||||
static constexpr size_t GATTS_PARAM_SIZE = 32;
|
||||
|
||||
// Maximum size for inline storage of data
|
||||
// GATTC: 80 - 28 (param) - 8 (other fields) = 44 bytes for data
|
||||
// GATTS: 80 - 32 (param) - 8 (other fields) = 40 bytes for data
|
||||
static constexpr size_t GATTC_INLINE_DATA_SIZE = 44;
|
||||
static constexpr size_t GATTS_INLINE_DATA_SIZE = 40;
|
||||
|
||||
// Verify param struct sizes
|
||||
static_assert(sizeof(esp_ble_gattc_cb_param_t) == GATTC_PARAM_SIZE, "GATTC param size unexpected");
|
||||
static_assert(sizeof(esp_ble_gatts_cb_param_t) == GATTS_PARAM_SIZE, "GATTS param size unexpected");
|
||||
|
||||
// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
|
||||
// This class stores each event with minimal memory usage.
|
||||
// GAP events (99% of traffic) don't have the heap allocation overhead.
|
||||
// GATTC/GATTS events use heap allocation for their param and inline storage for small data.
|
||||
// GATTC/GATTS events use heap allocation for their param and data.
|
||||
//
|
||||
// Event flow:
|
||||
// 1. ESP-IDF BLE stack calls our static handlers in the BLE task context
|
||||
@@ -125,21 +111,21 @@ class BLEEvent {
|
||||
this->init_gap_data_(e, p);
|
||||
}
|
||||
|
||||
// Constructor for GATTC events - param stored inline, data may use heap
|
||||
// IMPORTANT: We MUST copy the param struct because the pointer from ESP-IDF
|
||||
// is only valid during the callback execution. Since BLE events are processed
|
||||
// asynchronously in the main loop, we store our own copy inline to ensure
|
||||
// the data remains valid until the event is processed.
|
||||
// Constructor for GATTC events - uses heap allocation
|
||||
// IMPORTANT: The heap allocation is REQUIRED and must not be removed as an optimization.
|
||||
// The param pointer from ESP-IDF is only valid during the callback execution.
|
||||
// Since BLE events are processed asynchronously in the main loop, we must create
|
||||
// our own copy to ensure the data remains valid until the event is processed.
|
||||
BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
|
||||
this->type_ = GATTC;
|
||||
this->init_gattc_data_(e, i, p);
|
||||
}
|
||||
|
||||
// Constructor for GATTS events - param stored inline, data may use heap
|
||||
// IMPORTANT: We MUST copy the param struct because the pointer from ESP-IDF
|
||||
// is only valid during the callback execution. Since BLE events are processed
|
||||
// asynchronously in the main loop, we store our own copy inline to ensure
|
||||
// the data remains valid until the event is processed.
|
||||
// Constructor for GATTS events - uses heap allocation
|
||||
// IMPORTANT: The heap allocation is REQUIRED and must not be removed as an optimization.
|
||||
// The param pointer from ESP-IDF is only valid during the callback execution.
|
||||
// Since BLE events are processed asynchronously in the main loop, we must create
|
||||
// our own copy to ensure the data remains valid until the event is processed.
|
||||
BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
|
||||
this->type_ = GATTS;
|
||||
this->init_gatts_data_(e, i, p);
|
||||
@@ -149,32 +135,27 @@ class BLEEvent {
|
||||
~BLEEvent() { this->release(); }
|
||||
|
||||
// Default constructor for pre-allocation in pool
|
||||
BLEEvent() : event_{}, type_(GAP) {}
|
||||
BLEEvent() : type_(GAP) {}
|
||||
|
||||
// Invoked on return to EventPool - clean up any heap-allocated data
|
||||
void release() {
|
||||
switch (this->type_) {
|
||||
case GAP:
|
||||
// GAP events don't have heap allocations
|
||||
break;
|
||||
case GATTC:
|
||||
// Param is now stored inline, only delete heap data if it was heap-allocated
|
||||
if (!this->event_.gattc.is_inline && this->event_.gattc.data.heap_data != nullptr) {
|
||||
delete[] this->event_.gattc.data.heap_data;
|
||||
}
|
||||
// Clear critical fields to prevent issues if type changes
|
||||
this->event_.gattc.is_inline = false;
|
||||
this->event_.gattc.data.heap_data = nullptr;
|
||||
break;
|
||||
case GATTS:
|
||||
// Param is now stored inline, only delete heap data if it was heap-allocated
|
||||
if (!this->event_.gatts.is_inline && this->event_.gatts.data.heap_data != nullptr) {
|
||||
delete[] this->event_.gatts.data.heap_data;
|
||||
}
|
||||
// Clear critical fields to prevent issues if type changes
|
||||
this->event_.gatts.is_inline = false;
|
||||
this->event_.gatts.data.heap_data = nullptr;
|
||||
break;
|
||||
if (this->type_ == GAP) {
|
||||
return;
|
||||
}
|
||||
if (this->type_ == GATTC) {
|
||||
delete this->event_.gattc.gattc_param;
|
||||
delete[] this->event_.gattc.data;
|
||||
this->event_.gattc.gattc_param = nullptr;
|
||||
this->event_.gattc.data = nullptr;
|
||||
this->event_.gattc.data_len = 0;
|
||||
return;
|
||||
}
|
||||
if (this->type_ == GATTS) {
|
||||
delete this->event_.gatts.gatts_param;
|
||||
delete[] this->event_.gatts.data;
|
||||
this->event_.gatts.gatts_param = nullptr;
|
||||
this->event_.gatts.data = nullptr;
|
||||
this->event_.gatts.data_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,30 +207,22 @@ class BLEEvent {
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
struct gattc_event {
|
||||
esp_ble_gattc_cb_param_t gattc_param; // Stored inline (28 bytes)
|
||||
esp_gattc_cb_event_t gattc_event; // 4 bytes
|
||||
union {
|
||||
uint8_t *heap_data; // 4 bytes when heap-allocated
|
||||
uint8_t inline_data[GATTC_INLINE_DATA_SIZE]; // 44 bytes when stored inline
|
||||
} data; // 44 bytes total
|
||||
uint16_t data_len; // 2 bytes
|
||||
esp_gatt_if_t gattc_if; // 1 byte
|
||||
bool is_inline; // 1 byte - true when data is stored inline
|
||||
} gattc; // Total: 80 bytes
|
||||
esp_gattc_cb_event_t gattc_event;
|
||||
esp_gatt_if_t gattc_if;
|
||||
esp_ble_gattc_cb_param_t *gattc_param; // Heap-allocated
|
||||
uint8_t *data; // Heap-allocated raw buffer (manually managed)
|
||||
uint16_t data_len; // Track size separately
|
||||
} gattc;
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
struct gatts_event {
|
||||
esp_ble_gatts_cb_param_t gatts_param; // Stored inline (32 bytes)
|
||||
esp_gatts_cb_event_t gatts_event; // 4 bytes
|
||||
union {
|
||||
uint8_t *heap_data; // 4 bytes when heap-allocated
|
||||
uint8_t inline_data[GATTS_INLINE_DATA_SIZE]; // 40 bytes when stored inline
|
||||
} data; // 40 bytes total
|
||||
uint16_t data_len; // 2 bytes
|
||||
esp_gatt_if_t gatts_if; // 1 byte
|
||||
bool is_inline; // 1 byte - true when data is stored inline
|
||||
} gatts; // Total: 80 bytes
|
||||
} event_; // 80 bytes
|
||||
esp_gatts_cb_event_t gatts_event;
|
||||
esp_gatt_if_t gatts_if;
|
||||
esp_ble_gatts_cb_param_t *gatts_param; // Heap-allocated
|
||||
uint8_t *data; // Heap-allocated raw buffer (manually managed)
|
||||
uint16_t data_len; // Track size separately
|
||||
} gatts;
|
||||
} event_; // 80 bytes
|
||||
|
||||
ble_event_t type_;
|
||||
|
||||
@@ -263,29 +236,6 @@ class BLEEvent {
|
||||
const esp_ble_sec_t &security() const { return event_.gap.security; }
|
||||
|
||||
private:
|
||||
// Helper to copy data with inline storage optimization
|
||||
template<typename EventStruct, size_t InlineSize>
|
||||
void copy_data_with_inline_storage_(EventStruct &event, const uint8_t *src_data, uint16_t len,
|
||||
uint8_t **param_value_ptr) {
|
||||
event.data_len = len;
|
||||
if (len > 0) {
|
||||
if (len <= InlineSize) {
|
||||
event.is_inline = true;
|
||||
memcpy(event.data.inline_data, src_data, len);
|
||||
*param_value_ptr = event.data.inline_data;
|
||||
} else {
|
||||
event.is_inline = false;
|
||||
event.data.heap_data = new uint8_t[len];
|
||||
memcpy(event.data.heap_data, src_data, len);
|
||||
*param_value_ptr = event.data.heap_data;
|
||||
}
|
||||
} else {
|
||||
event.is_inline = false;
|
||||
event.data.heap_data = nullptr;
|
||||
*param_value_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize GAP event data
|
||||
void init_gap_data_(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
|
||||
this->event_.gap.gap_event = e;
|
||||
@@ -370,37 +320,48 @@ class BLEEvent {
|
||||
this->event_.gattc.gattc_if = i;
|
||||
|
||||
if (p == nullptr) {
|
||||
// Zero out the param struct when null
|
||||
memset(&this->event_.gattc.gattc_param, 0, sizeof(this->event_.gattc.gattc_param));
|
||||
this->event_.gattc.is_inline = false;
|
||||
this->event_.gattc.data.heap_data = nullptr;
|
||||
this->event_.gattc.gattc_param = nullptr;
|
||||
this->event_.gattc.data = nullptr;
|
||||
this->event_.gattc.data_len = 0;
|
||||
return; // Invalid event, but we can't log in header file
|
||||
}
|
||||
|
||||
// Copy param struct inline (no heap allocation!)
|
||||
// GATTC/GATTS events are rare (<1% of events) but we can still store them inline
|
||||
// along with small data payloads, eliminating all heap allocations for typical BLE operations
|
||||
// CRITICAL: This copy is REQUIRED for memory safety - the ESP-IDF param pointer
|
||||
// is only valid during the callback and will be reused/freed after we return
|
||||
this->event_.gattc.gattc_param = *p;
|
||||
// Heap-allocate param and data
|
||||
// Heap allocation is used because GATTC/GATTS events are rare (<1% of events)
|
||||
// while GAP events (99%) are stored inline to minimize memory usage
|
||||
// IMPORTANT: This heap allocation provides clear ownership semantics:
|
||||
// - The BLEEvent owns the allocated memory for its lifetime
|
||||
// - The data remains valid from the BLE callback context until processed in the main loop
|
||||
// - Without this copy, we'd have use-after-free bugs as ESP-IDF reuses the callback memory
|
||||
this->event_.gattc.gattc_param = new esp_ble_gattc_cb_param_t(*p);
|
||||
|
||||
// Copy data for events that need it
|
||||
// The param struct contains pointers (e.g., notify.value) that point to temporary buffers.
|
||||
// We must copy this data to ensure it remains valid when the event is processed later.
|
||||
switch (e) {
|
||||
case ESP_GATTC_NOTIFY_EVT:
|
||||
copy_data_with_inline_storage_<decltype(this->event_.gattc), GATTC_INLINE_DATA_SIZE>(
|
||||
this->event_.gattc, p->notify.value, p->notify.value_len, &this->event_.gattc.gattc_param.notify.value);
|
||||
this->event_.gattc.data_len = p->notify.value_len;
|
||||
if (p->notify.value_len > 0) {
|
||||
this->event_.gattc.data = new uint8_t[p->notify.value_len];
|
||||
memcpy(this->event_.gattc.data, p->notify.value, p->notify.value_len);
|
||||
} else {
|
||||
this->event_.gattc.data = nullptr;
|
||||
}
|
||||
this->event_.gattc.gattc_param->notify.value = this->event_.gattc.data;
|
||||
break;
|
||||
case ESP_GATTC_READ_CHAR_EVT:
|
||||
case ESP_GATTC_READ_DESCR_EVT:
|
||||
copy_data_with_inline_storage_<decltype(this->event_.gattc), GATTC_INLINE_DATA_SIZE>(
|
||||
this->event_.gattc, p->read.value, p->read.value_len, &this->event_.gattc.gattc_param.read.value);
|
||||
this->event_.gattc.data_len = p->read.value_len;
|
||||
if (p->read.value_len > 0) {
|
||||
this->event_.gattc.data = new uint8_t[p->read.value_len];
|
||||
memcpy(this->event_.gattc.data, p->read.value, p->read.value_len);
|
||||
} else {
|
||||
this->event_.gattc.data = nullptr;
|
||||
}
|
||||
this->event_.gattc.gattc_param->read.value = this->event_.gattc.data;
|
||||
break;
|
||||
default:
|
||||
this->event_.gattc.is_inline = false;
|
||||
this->event_.gattc.data.heap_data = nullptr;
|
||||
this->event_.gattc.data = nullptr;
|
||||
this->event_.gattc.data_len = 0;
|
||||
break;
|
||||
}
|
||||
@@ -412,32 +373,37 @@ class BLEEvent {
|
||||
this->event_.gatts.gatts_if = i;
|
||||
|
||||
if (p == nullptr) {
|
||||
// Zero out the param struct when null
|
||||
memset(&this->event_.gatts.gatts_param, 0, sizeof(this->event_.gatts.gatts_param));
|
||||
this->event_.gatts.is_inline = false;
|
||||
this->event_.gatts.data.heap_data = nullptr;
|
||||
this->event_.gatts.gatts_param = nullptr;
|
||||
this->event_.gatts.data = nullptr;
|
||||
this->event_.gatts.data_len = 0;
|
||||
return; // Invalid event, but we can't log in header file
|
||||
}
|
||||
|
||||
// Copy param struct inline (no heap allocation!)
|
||||
// GATTC/GATTS events are rare (<1% of events) but we can still store them inline
|
||||
// along with small data payloads, eliminating all heap allocations for typical BLE operations
|
||||
// CRITICAL: This copy is REQUIRED for memory safety - the ESP-IDF param pointer
|
||||
// is only valid during the callback and will be reused/freed after we return
|
||||
this->event_.gatts.gatts_param = *p;
|
||||
// Heap-allocate param and data
|
||||
// Heap allocation is used because GATTC/GATTS events are rare (<1% of events)
|
||||
// while GAP events (99%) are stored inline to minimize memory usage
|
||||
// IMPORTANT: This heap allocation provides clear ownership semantics:
|
||||
// - The BLEEvent owns the allocated memory for its lifetime
|
||||
// - The data remains valid from the BLE callback context until processed in the main loop
|
||||
// - Without this copy, we'd have use-after-free bugs as ESP-IDF reuses the callback memory
|
||||
this->event_.gatts.gatts_param = new esp_ble_gatts_cb_param_t(*p);
|
||||
|
||||
// Copy data for events that need it
|
||||
// The param struct contains pointers (e.g., write.value) that point to temporary buffers.
|
||||
// We must copy this data to ensure it remains valid when the event is processed later.
|
||||
switch (e) {
|
||||
case ESP_GATTS_WRITE_EVT:
|
||||
copy_data_with_inline_storage_<decltype(this->event_.gatts), GATTS_INLINE_DATA_SIZE>(
|
||||
this->event_.gatts, p->write.value, p->write.len, &this->event_.gatts.gatts_param.write.value);
|
||||
this->event_.gatts.data_len = p->write.len;
|
||||
if (p->write.len > 0) {
|
||||
this->event_.gatts.data = new uint8_t[p->write.len];
|
||||
memcpy(this->event_.gatts.data, p->write.value, p->write.len);
|
||||
} else {
|
||||
this->event_.gatts.data = nullptr;
|
||||
}
|
||||
this->event_.gatts.gatts_param->write.value = this->event_.gatts.data;
|
||||
break;
|
||||
default:
|
||||
this->event_.gatts.is_inline = false;
|
||||
this->event_.gatts.data.heap_data = nullptr;
|
||||
this->event_.gatts.data = nullptr;
|
||||
this->event_.gatts.data_len = 0;
|
||||
break;
|
||||
}
|
||||
@@ -448,15 +414,6 @@ class BLEEvent {
|
||||
// The gap member in the union should be 80 bytes (including the gap_event enum)
|
||||
static_assert(sizeof(decltype(((BLEEvent *) nullptr)->event_.gap)) <= 80, "gap_event struct has grown beyond 80 bytes");
|
||||
|
||||
// Verify GATTC and GATTS structs don't exceed GAP struct size
|
||||
// This ensures the union size is determined by GAP (the most common event type)
|
||||
static_assert(sizeof(decltype(((BLEEvent *) nullptr)->event_.gattc)) <=
|
||||
sizeof(decltype(((BLEEvent *) nullptr)->event_.gap)),
|
||||
"gattc_event struct exceeds gap_event size - union size would increase");
|
||||
static_assert(sizeof(decltype(((BLEEvent *) nullptr)->event_.gatts)) <=
|
||||
sizeof(decltype(((BLEEvent *) nullptr)->event_.gap)),
|
||||
"gatts_event struct exceeds gap_event size - union size would increase");
|
||||
|
||||
// Verify esp_ble_sec_t fits within our union
|
||||
static_assert(sizeof(esp_ble_sec_t) <= 73, "esp_ble_sec_t is larger than BLEScanResult");
|
||||
|
||||
|
@@ -92,7 +92,7 @@ bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) {
|
||||
return false;
|
||||
if (this->address_ == 0 || device.address_uint64() != this->address_)
|
||||
return false;
|
||||
if (this->state_ != espbt::ClientState::IDLE)
|
||||
if (this->state_ != espbt::ClientState::IDLE && this->state_ != espbt::ClientState::SEARCHING)
|
||||
return false;
|
||||
|
||||
this->log_event_("Found device");
|
||||
@@ -111,8 +111,36 @@ void BLEClientBase::connect() {
|
||||
this->remote_addr_type_);
|
||||
this->paired_ = false;
|
||||
|
||||
// Open the connection without setting connection parameters
|
||||
// Parameters will be set after connection is established if needed
|
||||
// Set preferred connection parameters before connecting
|
||||
// Use FAST for all V3 connections (better latency and reliability)
|
||||
// Use MEDIUM for V1/legacy connections (balanced performance)
|
||||
uint16_t min_interval, max_interval, timeout;
|
||||
const char *param_type;
|
||||
|
||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE ||
|
||||
this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
|
||||
min_interval = FAST_MIN_CONN_INTERVAL;
|
||||
max_interval = FAST_MAX_CONN_INTERVAL;
|
||||
timeout = FAST_CONN_TIMEOUT;
|
||||
param_type = "fast";
|
||||
} else {
|
||||
min_interval = MEDIUM_MIN_CONN_INTERVAL;
|
||||
max_interval = MEDIUM_MAX_CONN_INTERVAL;
|
||||
timeout = MEDIUM_CONN_TIMEOUT;
|
||||
param_type = "medium";
|
||||
}
|
||||
|
||||
auto param_ret = esp_ble_gap_set_prefer_conn_params(this->remote_bda_, min_interval, max_interval,
|
||||
0, // latency: 0
|
||||
timeout);
|
||||
if (param_ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "[%d] [%s] esp_ble_gap_set_prefer_conn_params failed: %d", this->connection_index_,
|
||||
this->address_str_.c_str(), param_ret);
|
||||
} else {
|
||||
this->log_connection_params_(param_type);
|
||||
}
|
||||
|
||||
// Now open the connection
|
||||
auto ret = esp_ble_gattc_open(this->gattc_if_, this->remote_bda_, this->remote_addr_type_, true);
|
||||
if (ret) {
|
||||
this->log_gattc_warning_("esp_ble_gattc_open", ret);
|
||||
@@ -131,7 +159,7 @@ void BLEClientBase::disconnect() {
|
||||
return;
|
||||
}
|
||||
if (this->state_ == espbt::ClientState::CONNECTING || this->conn_id_ == UNSET_CONN_ID) {
|
||||
ESP_LOGD(TAG, "[%d] [%s] Disconnect before connected, disconnect scheduled", this->connection_index_,
|
||||
ESP_LOGW(TAG, "[%d] [%s] Disconnecting before connected, disconnect scheduled.", this->connection_index_,
|
||||
this->address_str_.c_str());
|
||||
this->want_disconnect_ = true;
|
||||
return;
|
||||
@@ -144,11 +172,13 @@ void BLEClientBase::unconditional_disconnect() {
|
||||
ESP_LOGI(TAG, "[%d] [%s] Disconnecting (conn_id: %d).", this->connection_index_, this->address_str_.c_str(),
|
||||
this->conn_id_);
|
||||
if (this->state_ == espbt::ClientState::DISCONNECTING) {
|
||||
this->log_error_("Already disconnecting");
|
||||
ESP_LOGE(TAG, "[%d] [%s] Tried to disconnect while already disconnecting.", this->connection_index_,
|
||||
this->address_str_.c_str());
|
||||
return;
|
||||
}
|
||||
if (this->conn_id_ == UNSET_CONN_ID) {
|
||||
this->log_error_("conn id unset, cannot disconnect");
|
||||
ESP_LOGE(TAG, "[%d] [%s] No connection ID set, cannot disconnect.", this->connection_index_,
|
||||
this->address_str_.c_str());
|
||||
return;
|
||||
}
|
||||
auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_);
|
||||
@@ -163,7 +193,8 @@ void BLEClientBase::unconditional_disconnect() {
|
||||
this->log_gattc_warning_("esp_ble_gattc_close", err);
|
||||
}
|
||||
|
||||
if (this->state_ == espbt::ClientState::READY_TO_CONNECT || this->state_ == espbt::ClientState::DISCOVERED) {
|
||||
if (this->state_ == espbt::ClientState::SEARCHING || this->state_ == espbt::ClientState::READY_TO_CONNECT ||
|
||||
this->state_ == espbt::ClientState::DISCOVERED) {
|
||||
this->set_address(0);
|
||||
this->set_state(espbt::ClientState::IDLE);
|
||||
} else {
|
||||
@@ -203,33 +234,8 @@ void BLEClientBase::log_connection_params_(const char *param_type) {
|
||||
ESP_LOGD(TAG, "[%d] [%s] %s conn params", this->connection_index_, this->address_str_.c_str(), param_type);
|
||||
}
|
||||
|
||||
void BLEClientBase::log_error_(const char *message) {
|
||||
ESP_LOGE(TAG, "[%d] [%s] %s", this->connection_index_, this->address_str_.c_str(), message);
|
||||
}
|
||||
|
||||
void BLEClientBase::log_error_(const char *message, int code) {
|
||||
ESP_LOGE(TAG, "[%d] [%s] %s=%d", this->connection_index_, this->address_str_.c_str(), message, code);
|
||||
}
|
||||
|
||||
void BLEClientBase::log_warning_(const char *message) {
|
||||
ESP_LOGW(TAG, "[%d] [%s] %s", this->connection_index_, this->address_str_.c_str(), message);
|
||||
}
|
||||
|
||||
void BLEClientBase::set_fast_conn_params_() {
|
||||
// Switch to fast connection parameters for service discovery
|
||||
// This improves discovery speed for devices with short timeouts
|
||||
esp_ble_conn_update_params_t conn_params = {{0}};
|
||||
memcpy(conn_params.bda, this->remote_bda_, sizeof(esp_bd_addr_t));
|
||||
conn_params.min_int = FAST_MIN_CONN_INTERVAL;
|
||||
conn_params.max_int = FAST_MAX_CONN_INTERVAL;
|
||||
conn_params.latency = 0;
|
||||
conn_params.timeout = FAST_CONN_TIMEOUT;
|
||||
this->log_connection_params_("fast");
|
||||
esp_ble_gap_update_conn_params(&conn_params);
|
||||
}
|
||||
|
||||
void BLEClientBase::set_medium_conn_params_() {
|
||||
// Set medium connection parameters for balanced performance
|
||||
void BLEClientBase::restore_medium_conn_params_() {
|
||||
// Restore to medium connection parameters after initial connection phase
|
||||
// This balances performance with bandwidth usage for normal operation
|
||||
esp_ble_conn_update_params_t conn_params = {{0}};
|
||||
memcpy(conn_params.bda, this->remote_bda_, sizeof(esp_bd_addr_t));
|
||||
@@ -258,7 +264,8 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
this->app_id);
|
||||
this->gattc_if_ = esp_gattc_if;
|
||||
} else {
|
||||
this->log_error_("gattc app registration failed status", param->reg.status);
|
||||
ESP_LOGE(TAG, "[%d] [%s] gattc app registration failed id=%d code=%d", this->connection_index_,
|
||||
this->address_str_.c_str(), param->reg.app_id, param->reg.status);
|
||||
this->status_ = param->reg.status;
|
||||
this->mark_failed();
|
||||
}
|
||||
@@ -270,21 +277,11 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
this->log_gattc_event_("OPEN");
|
||||
// conn_id was already set in ESP_GATTC_CONNECT_EVT
|
||||
this->service_count_ = 0;
|
||||
|
||||
// ESP-IDF's BLE stack may send ESP_GATTC_OPEN_EVT after esp_ble_gattc_open() returns an
|
||||
// error, if the error occurred at the BTA/GATT layer. This can result in the event
|
||||
// arriving after we've already transitioned to IDLE state.
|
||||
if (this->state_ == espbt::ClientState::IDLE) {
|
||||
ESP_LOGD(TAG, "[%d] [%s] ESP_GATTC_OPEN_EVT in IDLE state (status=%d), ignoring", this->connection_index_,
|
||||
this->address_str_.c_str(), param->open.status);
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->state_ != espbt::ClientState::CONNECTING) {
|
||||
// This should not happen but lets log it in case it does
|
||||
// because it means we have a bad assumption about how the
|
||||
// ESP BT stack works.
|
||||
ESP_LOGE(TAG, "[%d] [%s] Got ESP_GATTC_OPEN_EVT in %s state (status=%d)", this->connection_index_,
|
||||
ESP_LOGE(TAG, "[%d] [%s] Got ESP_GATTC_OPEN_EVT while in %s state, status=%d", this->connection_index_,
|
||||
this->address_str_.c_str(), espbt::client_state_to_string(this->state_), param->open.status);
|
||||
}
|
||||
if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) {
|
||||
@@ -304,20 +301,13 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
this->set_state(espbt::ClientState::CONNECTED);
|
||||
ESP_LOGI(TAG, "[%d] [%s] Connection open", this->connection_index_, this->address_str_.c_str());
|
||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
|
||||
// Cached connections use medium connection parameters
|
||||
this->set_medium_conn_params_();
|
||||
// Restore to medium connection parameters for cached connections too
|
||||
this->restore_medium_conn_params_();
|
||||
// only set our state, subclients might have more stuff to do yet.
|
||||
this->state_ = espbt::ClientState::ESTABLISHED;
|
||||
break;
|
||||
}
|
||||
// For V3_WITHOUT_CACHE, switch to fast params for service discovery
|
||||
// Service discovery period is critical - we typically have only 10s to complete
|
||||
// discovery before the device disconnects us. Fast connection parameters are
|
||||
// essential to finish service resolution in time and avoid retry loops.
|
||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
|
||||
this->set_fast_conn_params_();
|
||||
}
|
||||
this->log_event_("Searching for services");
|
||||
ESP_LOGD(TAG, "[%d] [%s] Searching for services", this->connection_index_, this->address_str_.c_str());
|
||||
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
|
||||
break;
|
||||
}
|
||||
@@ -342,7 +332,8 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
// Check if we were disconnected while waiting for service discovery
|
||||
if (param->disconnect.reason == ESP_GATT_CONN_TERMINATE_PEER_USER &&
|
||||
this->state_ == espbt::ClientState::CONNECTED) {
|
||||
this->log_warning_("Remote closed during discovery");
|
||||
ESP_LOGW(TAG, "[%d] [%s] Disconnected by remote during service discovery", this->connection_index_,
|
||||
this->address_str_.c_str());
|
||||
} else {
|
||||
ESP_LOGD(TAG, "[%d] [%s] ESP_GATTC_DISCONNECT_EVT, reason 0x%02x", this->connection_index_,
|
||||
this->address_str_.c_str(), param->disconnect.reason);
|
||||
@@ -379,8 +370,7 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
if (this->conn_id_ != param->search_res.conn_id)
|
||||
return false;
|
||||
this->service_count_++;
|
||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE ||
|
||||
this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
|
||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
|
||||
// V3 clients don't need services initialized since
|
||||
// as they use the ESP APIs to get services.
|
||||
break;
|
||||
@@ -399,11 +389,12 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
if (this->conn_id_ != param->search_cmpl.conn_id)
|
||||
return false;
|
||||
this->log_gattc_event_("SEARCH_CMPL");
|
||||
// For V3_WITHOUT_CACHE, switch back to medium connection parameters after service discovery
|
||||
// For V3 connections, restore to medium connection parameters after service discovery
|
||||
// This balances performance with bandwidth usage after the critical discovery phase
|
||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
|
||||
this->set_medium_conn_params_();
|
||||
} else if (this->connection_type_ != espbt::ConnectionType::V3_WITH_CACHE) {
|
||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE ||
|
||||
this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
|
||||
this->restore_medium_conn_params_();
|
||||
} else {
|
||||
#ifdef USE_ESP32_BLE_DEVICE
|
||||
for (auto &svc : this->services_) {
|
||||
ESP_LOGV(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_.c_str(),
|
||||
@@ -487,11 +478,6 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
|
||||
this->log_gattc_event_("UNREG_FOR_NOTIFY");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// ideally would check all other events for matching conn_id
|
||||
ESP_LOGD(TAG, "[%d] [%s] Event %d", this->connection_index_, this->address_str_.c_str(), event);
|
||||
@@ -520,14 +506,16 @@ void BLEClientBase::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_
|
||||
return;
|
||||
esp_bd_addr_t bd_addr;
|
||||
memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
|
||||
ESP_LOGI(TAG, "[%d] [%s] auth complete addr: %s", this->connection_index_, this->address_str_.c_str(),
|
||||
ESP_LOGI(TAG, "[%d] [%s] auth complete. remote BD_ADDR: %s", this->connection_index_, this->address_str_.c_str(),
|
||||
format_hex(bd_addr, 6).c_str());
|
||||
if (!param->ble_security.auth_cmpl.success) {
|
||||
this->log_error_("auth fail reason", param->ble_security.auth_cmpl.fail_reason);
|
||||
ESP_LOGE(TAG, "[%d] [%s] auth fail reason = 0x%x", this->connection_index_, this->address_str_.c_str(),
|
||||
param->ble_security.auth_cmpl.fail_reason);
|
||||
} else {
|
||||
this->paired_ = true;
|
||||
ESP_LOGD(TAG, "[%d] [%s] auth success type = %d mode = %d", this->connection_index_, this->address_str_.c_str(),
|
||||
param->ble_security.auth_cmpl.addr_type, param->ble_security.auth_cmpl.auth_mode);
|
||||
ESP_LOGD(TAG, "[%d] [%s] auth success. address type = %d auth mode = %d", this->connection_index_,
|
||||
this->address_str_.c_str(), param->ble_security.auth_cmpl.addr_type,
|
||||
param->ble_security.auth_cmpl.auth_mode);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@@ -133,15 +133,10 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
|
||||
|
||||
void log_event_(const char *name);
|
||||
void log_gattc_event_(const char *name);
|
||||
void set_fast_conn_params_();
|
||||
void set_medium_conn_params_();
|
||||
void restore_medium_conn_params_();
|
||||
void log_gattc_warning_(const char *operation, esp_gatt_status_t status);
|
||||
void log_gattc_warning_(const char *operation, esp_err_t err);
|
||||
void log_connection_params_(const char *param_type);
|
||||
// Compact error logging helpers to reduce flash usage
|
||||
void log_error_(const char *message);
|
||||
void log_error_(const char *message, int code);
|
||||
void log_warning_(const char *message);
|
||||
};
|
||||
|
||||
} // namespace esphome::esp32_ble_client
|
||||
|
@@ -49,6 +49,8 @@ const char *client_state_to_string(ClientState state) {
|
||||
return "DISCONNECTING";
|
||||
case ClientState::IDLE:
|
||||
return "IDLE";
|
||||
case ClientState::SEARCHING:
|
||||
return "SEARCHING";
|
||||
case ClientState::DISCOVERED:
|
||||
return "DISCOVERED";
|
||||
case ClientState::READY_TO_CONNECT:
|
||||
@@ -134,8 +136,9 @@ void ESP32BLETracker::loop() {
|
||||
ClientStateCounts counts = this->count_client_states_();
|
||||
if (counts != this->client_state_counts_) {
|
||||
this->client_state_counts_ = counts;
|
||||
ESP_LOGD(TAG, "connecting: %d, discovered: %d, disconnecting: %d", this->client_state_counts_.connecting,
|
||||
this->client_state_counts_.discovered, this->client_state_counts_.disconnecting);
|
||||
ESP_LOGD(TAG, "connecting: %d, discovered: %d, searching: %d, disconnecting: %d",
|
||||
this->client_state_counts_.connecting, this->client_state_counts_.discovered,
|
||||
this->client_state_counts_.searching, this->client_state_counts_.disconnecting);
|
||||
}
|
||||
|
||||
if (this->scanner_state_ == ScannerState::FAILED ||
|
||||
@@ -155,8 +158,10 @@ void ESP32BLETracker::loop() {
|
||||
https://github.com/espressif/esp-idf/issues/6688
|
||||
|
||||
*/
|
||||
bool promote_to_connecting = counts.discovered && !counts.searching && !counts.connecting;
|
||||
|
||||
if (this->scanner_state_ == ScannerState::IDLE && !counts.connecting && !counts.disconnecting && !counts.discovered) {
|
||||
if (this->scanner_state_ == ScannerState::IDLE && !counts.connecting && !counts.disconnecting &&
|
||||
!promote_to_connecting) {
|
||||
#ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
|
||||
this->update_coex_preference_(false);
|
||||
#endif
|
||||
@@ -165,11 +170,12 @@ void ESP32BLETracker::loop() {
|
||||
}
|
||||
}
|
||||
// If there is a discovered client and no connecting
|
||||
// clients, then promote the discovered client to ready to connect.
|
||||
// clients and no clients using the scanner to search for
|
||||
// devices, then promote the discovered client to ready to connect.
|
||||
// We check both RUNNING and IDLE states because:
|
||||
// - RUNNING: gap_scan_event_handler initiates stop_scan_() but promotion can happen immediately
|
||||
// - IDLE: Scanner has already stopped (naturally or by gap_scan_event_handler)
|
||||
if (counts.discovered && !counts.connecting &&
|
||||
if (promote_to_connecting &&
|
||||
(this->scanner_state_ == ScannerState::RUNNING || this->scanner_state_ == ScannerState::IDLE)) {
|
||||
this->try_promote_discovered_clients_();
|
||||
}
|
||||
@@ -301,7 +307,14 @@ void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) {
|
||||
|
||||
if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
|
||||
// Process the scan result immediately
|
||||
this->process_scan_result_(scan_result);
|
||||
bool found_discovered_client = this->process_scan_result_(scan_result);
|
||||
|
||||
// If we found a discovered client that needs promotion, stop scanning
|
||||
// This replaces the promote_to_connecting logic from loop()
|
||||
if (found_discovered_client && this->scanner_state_ == ScannerState::RUNNING) {
|
||||
ESP_LOGD(TAG, "Found discovered client, stopping scan for connection");
|
||||
this->stop_scan_();
|
||||
}
|
||||
} else if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) {
|
||||
// Scan finished on its own
|
||||
if (this->scanner_state_ != ScannerState::RUNNING) {
|
||||
@@ -627,8 +640,9 @@ void ESP32BLETracker::dump_config() {
|
||||
this->scan_duration_, this->scan_interval_ * 0.625f, this->scan_window_ * 0.625f,
|
||||
this->scan_active_ ? "ACTIVE" : "PASSIVE", YESNO(this->scan_continuous_));
|
||||
ESP_LOGCONFIG(TAG, " Scanner State: %s", this->scanner_state_to_string_(this->scanner_state_));
|
||||
ESP_LOGCONFIG(TAG, " Connecting: %d, discovered: %d, disconnecting: %d", this->client_state_counts_.connecting,
|
||||
this->client_state_counts_.discovered, this->client_state_counts_.disconnecting);
|
||||
ESP_LOGCONFIG(TAG, " Connecting: %d, discovered: %d, searching: %d, disconnecting: %d",
|
||||
this->client_state_counts_.connecting, this->client_state_counts_.discovered,
|
||||
this->client_state_counts_.searching, this->client_state_counts_.disconnecting);
|
||||
if (this->scan_start_fail_count_) {
|
||||
ESP_LOGCONFIG(TAG, " Scan Start Fail Count: %d", this->scan_start_fail_count_);
|
||||
}
|
||||
@@ -706,9 +720,20 @@ bool ESPBTDevice::resolve_irk(const uint8_t *irk) const {
|
||||
ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
|
||||
}
|
||||
|
||||
bool ESP32BLETracker::has_connecting_clients_() const {
|
||||
for (auto *client : this->clients_) {
|
||||
auto state = client->state();
|
||||
if (state == ClientState::CONNECTING || state == ClientState::READY_TO_CONNECT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // USE_ESP32_BLE_DEVICE
|
||||
|
||||
void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
|
||||
bool ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
|
||||
bool found_discovered_client = false;
|
||||
|
||||
// Process raw advertisements
|
||||
if (this->raw_advertisements_) {
|
||||
for (auto *listener : this->listeners_) {
|
||||
@@ -734,6 +759,14 @@ void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
|
||||
for (auto *client : this->clients_) {
|
||||
if (client->parse_device(device)) {
|
||||
found = true;
|
||||
// Check if this client is discovered and needs promotion
|
||||
if (client->state() == ClientState::DISCOVERED) {
|
||||
// Only check for connecting clients if we found a discovered client
|
||||
// This matches the original logic: !connecting && client->state() == DISCOVERED
|
||||
if (!this->has_connecting_clients_()) {
|
||||
found_discovered_client = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,6 +775,8 @@ void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
|
||||
}
|
||||
#endif // USE_ESP32_BLE_DEVICE
|
||||
}
|
||||
|
||||
return found_discovered_client;
|
||||
}
|
||||
|
||||
void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) {
|
||||
|
@@ -141,10 +141,12 @@ class ESPBTDeviceListener {
|
||||
struct ClientStateCounts {
|
||||
uint8_t connecting = 0;
|
||||
uint8_t discovered = 0;
|
||||
uint8_t searching = 0;
|
||||
uint8_t disconnecting = 0;
|
||||
|
||||
bool operator==(const ClientStateCounts &other) const {
|
||||
return connecting == other.connecting && discovered == other.discovered && disconnecting == other.disconnecting;
|
||||
return connecting == other.connecting && discovered == other.discovered && searching == other.searching &&
|
||||
disconnecting == other.disconnecting;
|
||||
}
|
||||
|
||||
bool operator!=(const ClientStateCounts &other) const { return !(*this == other); }
|
||||
@@ -157,6 +159,8 @@ enum class ClientState : uint8_t {
|
||||
DISCONNECTING,
|
||||
// Connection is idle, no device detected.
|
||||
IDLE,
|
||||
// Searching for device.
|
||||
SEARCHING,
|
||||
// Device advertisement found.
|
||||
DISCOVERED,
|
||||
// Device is discovered and the scanner is stopped
|
||||
@@ -288,7 +292,12 @@ class ESP32BLETracker : public Component,
|
||||
/// Common cleanup logic when transitioning scanner to IDLE state
|
||||
void cleanup_scan_state_(bool is_stop_complete);
|
||||
/// Process a single scan result immediately
|
||||
void process_scan_result_(const BLEScanResult &scan_result);
|
||||
/// Returns true if a discovered client needs promotion to READY_TO_CONNECT
|
||||
bool process_scan_result_(const BLEScanResult &scan_result);
|
||||
#ifdef USE_ESP32_BLE_DEVICE
|
||||
/// Check if any clients are in connecting or ready to connect state
|
||||
bool has_connecting_clients_() const;
|
||||
#endif
|
||||
/// Handle scanner failure states
|
||||
void handle_scanner_failure_();
|
||||
/// Try to promote discovered clients to ready to connect
|
||||
@@ -312,6 +321,9 @@ class ESP32BLETracker : public Component,
|
||||
case ClientState::DISCOVERED:
|
||||
counts.discovered++;
|
||||
break;
|
||||
case ClientState::SEARCHING:
|
||||
counts.searching++;
|
||||
break;
|
||||
case ClientState::CONNECTING:
|
||||
case ClientState::READY_TO_CONNECT:
|
||||
counts.connecting++;
|
||||
|
@@ -12,7 +12,7 @@ extern "C" {
|
||||
#include "preferences.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace esp8266 {
|
||||
@@ -67,8 +67,6 @@ static uint32_t get_esp8266_flash_sector() {
|
||||
}
|
||||
static uint32_t get_esp8266_flash_address() { return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; }
|
||||
|
||||
static inline size_t bytes_to_words(size_t bytes) { return (bytes + 3) / 4; }
|
||||
|
||||
template<class It> uint32_t calculate_crc(It first, It last, uint32_t type) {
|
||||
uint32_t crc = type;
|
||||
while (first != last) {
|
||||
@@ -125,36 +123,41 @@ class ESP8266PreferenceBackend : public ESPPreferenceBackend {
|
||||
size_t length_words = 0;
|
||||
|
||||
bool save(const uint8_t *data, size_t len) override {
|
||||
if (bytes_to_words(len) != length_words) {
|
||||
if ((len + 3) / 4 != length_words) {
|
||||
return false;
|
||||
}
|
||||
size_t buffer_size = length_words + 1;
|
||||
std::unique_ptr<uint32_t[]> buffer(new uint32_t[buffer_size]()); // Note the () for zero-initialization
|
||||
memcpy(buffer.get(), data, len);
|
||||
buffer[length_words] = calculate_crc(buffer.get(), buffer.get() + length_words, type);
|
||||
std::vector<uint32_t> buffer;
|
||||
buffer.resize(length_words + 1);
|
||||
memcpy(buffer.data(), data, len);
|
||||
buffer[buffer.size() - 1] = calculate_crc(buffer.begin(), buffer.end() - 1, type);
|
||||
|
||||
if (in_flash) {
|
||||
return save_to_flash(offset, buffer.get(), buffer_size);
|
||||
return save_to_flash(offset, buffer.data(), buffer.size());
|
||||
} else {
|
||||
return save_to_rtc(offset, buffer.data(), buffer.size());
|
||||
}
|
||||
return save_to_rtc(offset, buffer.get(), buffer_size);
|
||||
}
|
||||
bool load(uint8_t *data, size_t len) override {
|
||||
if (bytes_to_words(len) != length_words) {
|
||||
if ((len + 3) / 4 != length_words) {
|
||||
return false;
|
||||
}
|
||||
size_t buffer_size = length_words + 1;
|
||||
std::unique_ptr<uint32_t[]> buffer(new uint32_t[buffer_size]());
|
||||
bool ret = in_flash ? load_from_flash(offset, buffer.get(), buffer_size)
|
||||
: load_from_rtc(offset, buffer.get(), buffer_size);
|
||||
std::vector<uint32_t> buffer;
|
||||
buffer.resize(length_words + 1);
|
||||
bool ret;
|
||||
if (in_flash) {
|
||||
ret = load_from_flash(offset, buffer.data(), buffer.size());
|
||||
} else {
|
||||
ret = load_from_rtc(offset, buffer.data(), buffer.size());
|
||||
}
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
uint32_t crc = calculate_crc(buffer.get(), buffer.get() + length_words, type);
|
||||
if (buffer[length_words] != crc) {
|
||||
uint32_t crc = calculate_crc(buffer.begin(), buffer.end() - 1, type);
|
||||
if (buffer[buffer.size() - 1] != crc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data, buffer.get(), len);
|
||||
memcpy(data, buffer.data(), len);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -175,7 +178,7 @@ class ESP8266Preferences : public ESPPreferences {
|
||||
}
|
||||
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
|
||||
uint32_t length_words = bytes_to_words(length);
|
||||
uint32_t length_words = (length + 3) / 4;
|
||||
if (in_flash) {
|
||||
uint32_t start = current_flash_offset;
|
||||
uint32_t end = start + length_words + 1;
|
||||
|
@@ -148,8 +148,7 @@ void Fan::publish_state() {
|
||||
constexpr uint32_t RESTORE_STATE_VERSION = 0x71700ABA;
|
||||
optional<FanRestoreState> Fan::restore_state_() {
|
||||
FanRestoreState recovered{};
|
||||
this->rtc_ =
|
||||
global_preferences->make_preference<FanRestoreState>(this->get_preference_hash() ^ RESTORE_STATE_VERSION);
|
||||
this->rtc_ = global_preferences->make_preference<FanRestoreState>(this->get_object_id_hash() ^ RESTORE_STATE_VERSION);
|
||||
bool restored = this->rtc_.load(&recovered);
|
||||
|
||||
switch (this->restore_mode_) {
|
||||
|
@@ -58,10 +58,10 @@ void GroveGasMultichannelV2Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Grove Multichannel Gas Sensor V2");
|
||||
LOG_I2C_DEVICE(this)
|
||||
LOG_UPDATE_INTERVAL(this)
|
||||
LOG_SENSOR(" ", "Nitrogen Dioxide", this->nitrogen_dioxide_sensor_);
|
||||
LOG_SENSOR(" ", "Ethanol", this->ethanol_sensor_);
|
||||
LOG_SENSOR(" ", "Carbon Monoxide", this->carbon_monoxide_sensor_);
|
||||
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
||||
LOG_SENSOR(" ", "Nitrogen Dioxide", this->nitrogen_dioxide_sensor_)
|
||||
LOG_SENSOR(" ", "Ethanol", this->ethanol_sensor_)
|
||||
LOG_SENSOR(" ", "Carbon Monoxide", this->carbon_monoxide_sensor_)
|
||||
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_)
|
||||
|
||||
if (this->is_failed()) {
|
||||
switch (this->error_code_) {
|
||||
|
@@ -351,7 +351,7 @@ ClimateTraits HaierClimateBase::traits() { return traits_; }
|
||||
void HaierClimateBase::initialization() {
|
||||
constexpr uint32_t restore_settings_version = 0xA77D21EF;
|
||||
this->base_rtc_ =
|
||||
global_preferences->make_preference<HaierBaseSettings>(this->get_preference_hash() ^ restore_settings_version);
|
||||
global_preferences->make_preference<HaierBaseSettings>(this->get_object_id_hash() ^ restore_settings_version);
|
||||
HaierBaseSettings recovered;
|
||||
if (!this->base_rtc_.load(&recovered)) {
|
||||
recovered = {false, true};
|
||||
|
@@ -516,7 +516,7 @@ void HonClimate::initialization() {
|
||||
HaierClimateBase::initialization();
|
||||
constexpr uint32_t restore_settings_version = 0x57EB59DDUL;
|
||||
this->hon_rtc_ =
|
||||
global_preferences->make_preference<HonSettings>(this->get_preference_hash() ^ restore_settings_version);
|
||||
global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
|
||||
HonSettings recovered;
|
||||
if (this->hon_rtc_.load(&recovered)) {
|
||||
this->settings_ = recovered;
|
||||
|
@@ -43,10 +43,10 @@ void HLW8012Component::dump_config() {
|
||||
" Voltage Divider: %.1f",
|
||||
this->change_mode_every_, this->current_resistor_ * 1000.0f, this->voltage_divider_);
|
||||
LOG_UPDATE_INTERVAL(this)
|
||||
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current", this->current_sensor_);
|
||||
LOG_SENSOR(" ", "Power", this->power_sensor_);
|
||||
LOG_SENSOR(" ", "Energy", this->energy_sensor_);
|
||||
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_)
|
||||
LOG_SENSOR(" ", "Current", this->current_sensor_)
|
||||
LOG_SENSOR(" ", "Power", this->power_sensor_)
|
||||
LOG_SENSOR(" ", "Energy", this->energy_sensor_)
|
||||
}
|
||||
float HLW8012Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
void HLW8012Component::update() {
|
||||
|
@@ -12,7 +12,7 @@ void HTE501Component::setup() {
|
||||
this->write(address, 2, false);
|
||||
uint8_t identification[9];
|
||||
this->read(identification, 9);
|
||||
if (identification[8] != crc8(identification, 8, 0xFF, 0x31, true)) {
|
||||
if (identification[8] != calc_crc8_(identification, 0, 7)) {
|
||||
this->error_code_ = CRC_CHECK_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
@@ -46,8 +46,7 @@ void HTE501Component::update() {
|
||||
this->set_timeout(50, [this]() {
|
||||
uint8_t i2c_response[6];
|
||||
this->read(i2c_response, 6);
|
||||
if (i2c_response[2] != crc8(i2c_response, 2, 0xFF, 0x31, true) &&
|
||||
i2c_response[5] != crc8(i2c_response + 3, 2, 0xFF, 0x31, true)) {
|
||||
if (i2c_response[2] != calc_crc8_(i2c_response, 0, 1) && i2c_response[5] != calc_crc8_(i2c_response, 3, 4)) {
|
||||
this->error_code_ = CRC_CHECK_FAILED;
|
||||
this->status_set_warning();
|
||||
return;
|
||||
@@ -68,5 +67,24 @@ void HTE501Component::update() {
|
||||
this->status_clear_warning();
|
||||
});
|
||||
}
|
||||
|
||||
unsigned char HTE501Component::calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to) {
|
||||
unsigned char crc_val = 0xFF;
|
||||
unsigned char i = 0;
|
||||
unsigned char j = 0;
|
||||
for (i = from; i <= to; i++) {
|
||||
int cur_val = buf[i];
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (((crc_val ^ cur_val) & 0x80) != 0) // If MSBs are not equal
|
||||
{
|
||||
crc_val = ((crc_val << 1) ^ 0x31);
|
||||
} else {
|
||||
crc_val = (crc_val << 1);
|
||||
}
|
||||
cur_val = cur_val << 1;
|
||||
}
|
||||
}
|
||||
return crc_val;
|
||||
}
|
||||
} // namespace hte501
|
||||
} // namespace esphome
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace hte501 {
|
||||
@@ -19,6 +19,7 @@ class HTE501Component : public PollingComponent, public i2c::I2CDevice {
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
unsigned char calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to);
|
||||
sensor::Sensor *temperature_sensor_;
|
||||
sensor::Sensor *humidity_sensor_;
|
||||
|
||||
|
@@ -159,7 +159,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ static const char *const TAG = "integration";
|
||||
|
||||
void IntegrationSensor::setup() {
|
||||
if (this->restore_) {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
|
||||
float preference_value = 0;
|
||||
this->pref_.load(&preference_value);
|
||||
this->result_ = preference_value;
|
||||
|
@@ -1,6 +1,5 @@
|
||||
#include "lc709203f.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "lc709203f.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace lc709203f {
|
||||
@@ -190,7 +189,7 @@ uint8_t Lc709203f::get_register_(uint8_t register_to_read, uint16_t *register_va
|
||||
// Error on the i2c bus
|
||||
this->status_set_warning(
|
||||
str_sprintf("Error code %d when reading from register 0x%02X", return_code, register_to_read).c_str());
|
||||
} else if (crc8(read_buffer, 5, 0x00, 0x07, true) != read_buffer[5]) {
|
||||
} else if (this->crc8_(read_buffer, 5) != read_buffer[5]) {
|
||||
// I2C indicated OK, but the CRC of the data does not matcth.
|
||||
this->status_set_warning(str_sprintf("CRC error reading from register 0x%02X", register_to_read).c_str());
|
||||
} else {
|
||||
@@ -221,7 +220,7 @@ uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set)
|
||||
write_buffer[1] = register_to_set;
|
||||
write_buffer[2] = value_to_set & 0xFF; // Low byte
|
||||
write_buffer[3] = (value_to_set >> 8) & 0xFF; // High byte
|
||||
write_buffer[4] = crc8(write_buffer, 4, 0x00, 0x07, true);
|
||||
write_buffer[4] = this->crc8_(write_buffer, 4);
|
||||
|
||||
for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) {
|
||||
// Note: we don't write the first byte of the write buffer to the device.
|
||||
@@ -240,6 +239,20 @@ uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set)
|
||||
return return_code;
|
||||
}
|
||||
|
||||
uint8_t Lc709203f::crc8_(uint8_t *byte_buffer, uint8_t length_of_crc) {
|
||||
uint8_t crc = 0x00;
|
||||
const uint8_t polynomial(0x07);
|
||||
|
||||
for (uint8_t j = length_of_crc; j; --j) {
|
||||
crc ^= *byte_buffer++;
|
||||
|
||||
for (uint8_t i = 8; i; --i) {
|
||||
crc = (crc & 0x80) ? (crc << 1) ^ polynomial : (crc << 1);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void Lc709203f::set_pack_size(uint16_t pack_size) {
|
||||
static const uint16_t PACK_SIZE_ARRAY[6] = {100, 200, 500, 1000, 2000, 3000};
|
||||
static const uint16_t APA_ARRAY[6] = {0x08, 0x0B, 0x10, 0x19, 0x2D, 0x36};
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace lc709203f {
|
||||
@@ -38,6 +38,7 @@ class Lc709203f : public sensor::Sensor, public PollingComponent, public i2c::I2
|
||||
private:
|
||||
uint8_t get_register_(uint8_t register_to_read, uint16_t *register_value);
|
||||
uint8_t set_register_(uint8_t register_to_set, uint16_t value_to_set);
|
||||
uint8_t crc8_(uint8_t *byte_buffer, uint8_t length_of_crc);
|
||||
|
||||
protected:
|
||||
sensor::Sensor *voltage_sensor_{nullptr};
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "ld2420_text_sensor.h"
|
||||
#include "text_sensor.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
@@ -184,7 +184,7 @@ static inline bool validate_header_footer(const uint8_t *header_footer, const ui
|
||||
void LD2450Component::setup() {
|
||||
#ifdef USE_NUMBER
|
||||
if (this->presence_timeout_number_ != nullptr) {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->presence_timeout_number_->get_preference_hash());
|
||||
this->pref_ = global_preferences->make_preference<float>(this->presence_timeout_number_->get_object_id_hash());
|
||||
this->set_presence_timeout();
|
||||
}
|
||||
#endif
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#include "esphome/core/preferences.h"
|
||||
#include <flashdb.h>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace esphome {
|
||||
@@ -139,29 +139,21 @@ class LibreTinyPreferences : public ESPPreferences {
|
||||
}
|
||||
|
||||
bool is_changed(const fdb_kvdb_t db, const NVSData &to_save) {
|
||||
NVSData stored_data{};
|
||||
struct fdb_kv kv;
|
||||
fdb_kv_t kvp = fdb_kv_get_obj(db, to_save.key.c_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());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check size first - if different, data has changed
|
||||
if (kv.value_len != to_save.data.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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);
|
||||
stored_data.data.resize(kv.value_len);
|
||||
fdb_blob_make(&blob, stored_data.data.data(), kv.value_len);
|
||||
size_t actual_len = fdb_kv_get_blob(db, to_save.key.c_str(), &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);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compare the actual data
|
||||
return memcmp(to_save.data.data(), stored_data.get(), kv.value_len) != 0;
|
||||
return to_save.data != stored_data.data;
|
||||
}
|
||||
|
||||
bool reset() override {
|
||||
|
@@ -44,13 +44,6 @@ class AddressableLightEffect : public LightEffect {
|
||||
this->apply(*this->get_addressable_(), current_color);
|
||||
}
|
||||
|
||||
/// Get effect index specifically for addressable effects.
|
||||
/// Can be used by effects to modify behavior based on their position in the list.
|
||||
uint32_t get_effect_index() const { return this->get_index(); }
|
||||
|
||||
/// Check if this is the currently running addressable effect.
|
||||
bool is_current_effect() const { return this->is_active() && this->get_addressable_()->is_effect_active(); }
|
||||
|
||||
protected:
|
||||
AddressableLight *get_addressable_() const { return (AddressableLight *) this->state_->get_output(); }
|
||||
};
|
||||
|
@@ -125,10 +125,6 @@ class LambdaLightEffect : public LightEffect {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current effect index for use in lambda functions.
|
||||
/// This can be useful for lambda effects that need to know their own index.
|
||||
uint32_t get_current_index() const { return this->get_index(); }
|
||||
|
||||
protected:
|
||||
std::function<void(bool initial_run)> f_;
|
||||
uint32_t update_interval_;
|
||||
@@ -147,10 +143,6 @@ class AutomationLightEffect : public LightEffect {
|
||||
}
|
||||
Trigger<> *get_trig() const { return trig_; }
|
||||
|
||||
/// Get the current effect index for use in automations.
|
||||
/// Useful for automations that need to know which effect is running.
|
||||
uint32_t get_current_index() const { return this->get_index(); }
|
||||
|
||||
protected:
|
||||
Trigger<> *trig_{new Trigger<>};
|
||||
};
|
||||
|
@@ -1,36 +0,0 @@
|
||||
#include "light_effect.h"
|
||||
#include "light_state.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
uint32_t LightEffect::get_index() const {
|
||||
if (this->state_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return this->get_index_in_parent_();
|
||||
}
|
||||
|
||||
bool LightEffect::is_active() const {
|
||||
if (this->state_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return this->get_index() != 0 && this->state_->get_current_effect_index() == this->get_index();
|
||||
}
|
||||
|
||||
uint32_t LightEffect::get_index_in_parent_() const {
|
||||
if (this->state_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto &effects = this->state_->get_effects();
|
||||
for (size_t i = 0; i < effects.size(); i++) {
|
||||
if (effects[i] == this) {
|
||||
return i + 1; // Effects are 1-indexed in the API
|
||||
}
|
||||
}
|
||||
return 0; // Not found
|
||||
}
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
@@ -34,23 +34,9 @@ class LightEffect {
|
||||
this->init();
|
||||
}
|
||||
|
||||
/// Get the index of this effect in the parent light's effect list.
|
||||
/// Returns 0 if not found or not initialized.
|
||||
uint32_t get_index() const;
|
||||
|
||||
/// Check if this effect is currently active.
|
||||
bool is_active() const;
|
||||
|
||||
/// Get a reference to the parent light state.
|
||||
/// Returns nullptr if not initialized.
|
||||
LightState *get_light_state() const { return this->state_; }
|
||||
|
||||
protected:
|
||||
LightState *state_{nullptr};
|
||||
std::string name_;
|
||||
|
||||
/// Internal method to find this effect's index in the parent light's effect list.
|
||||
uint32_t get_index_in_parent_() const;
|
||||
};
|
||||
|
||||
} // namespace light
|
||||
|
@@ -36,11 +36,8 @@ static constexpr const char *get_color_mode_json_str(ColorMode mode) {
|
||||
|
||||
void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
if (state.supports_effects()) {
|
||||
if (state.supports_effects())
|
||||
root["effect"] = state.get_effect_name();
|
||||
root["effect_index"] = state.get_current_effect_index();
|
||||
root["effect_count"] = state.get_effect_count();
|
||||
}
|
||||
|
||||
auto values = state.remote_values;
|
||||
auto traits = state.get_output()->get_traits();
|
||||
@@ -163,11 +160,6 @@ void LightJSONSchema::parse_json(LightState &state, LightCall &call, JsonObject
|
||||
const char *effect = root["effect"];
|
||||
call.set_effect(effect);
|
||||
}
|
||||
|
||||
if (root["effect_index"].is<uint32_t>()) {
|
||||
uint32_t effect_index = root["effect_index"];
|
||||
call.set_effect(effect_index);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace light
|
||||
|
@@ -41,7 +41,7 @@ void LightState::setup() {
|
||||
case LIGHT_RESTORE_DEFAULT_ON:
|
||||
case LIGHT_RESTORE_INVERTED_DEFAULT_OFF:
|
||||
case LIGHT_RESTORE_INVERTED_DEFAULT_ON:
|
||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_preference_hash());
|
||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_object_id_hash());
|
||||
// Attempt to load from preferences, else fall back to default values
|
||||
if (!this->rtc_.load(&recovered)) {
|
||||
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_DEFAULT_ON ||
|
||||
@@ -54,7 +54,7 @@ void LightState::setup() {
|
||||
break;
|
||||
case LIGHT_RESTORE_AND_OFF:
|
||||
case LIGHT_RESTORE_AND_ON:
|
||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_preference_hash());
|
||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_object_id_hash());
|
||||
this->rtc_.load(&recovered);
|
||||
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
|
||||
break;
|
||||
@@ -140,22 +140,12 @@ float LightState::get_setup_priority() const { return setup_priority::HARDWARE -
|
||||
void LightState::publish_state() { this->remote_values_callback_.call(); }
|
||||
|
||||
LightOutput *LightState::get_output() const { return this->output_; }
|
||||
|
||||
static constexpr const char *EFFECT_NONE = "None";
|
||||
static constexpr auto EFFECT_NONE_REF = StringRef::from_lit("None");
|
||||
|
||||
std::string LightState::get_effect_name() {
|
||||
if (this->active_effect_index_ > 0) {
|
||||
return this->effects_[this->active_effect_index_ - 1]->get_name();
|
||||
} else {
|
||||
return "None";
|
||||
}
|
||||
return EFFECT_NONE;
|
||||
}
|
||||
|
||||
StringRef LightState::get_effect_name_ref() {
|
||||
if (this->active_effect_index_ > 0) {
|
||||
return StringRef(this->effects_[this->active_effect_index_ - 1]->get_name());
|
||||
}
|
||||
return EFFECT_NONE_REF;
|
||||
}
|
||||
|
||||
void LightState::add_new_remote_values_callback(std::function<void()> &&send_callback) {
|
||||
|
@@ -4,7 +4,6 @@
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/optional.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
#include "light_call.h"
|
||||
#include "light_color_values.h"
|
||||
#include "light_effect.h"
|
||||
@@ -117,8 +116,6 @@ class LightState : public EntityBase, public Component {
|
||||
|
||||
/// Return the name of the current effect, or if no effect is active "None".
|
||||
std::string get_effect_name();
|
||||
/// Return the name of the current effect as StringRef (for API usage)
|
||||
StringRef get_effect_name_ref();
|
||||
|
||||
/**
|
||||
* This lets front-end components subscribe to light change events. This callback is called once
|
||||
@@ -163,44 +160,6 @@ class LightState : public EntityBase, public Component {
|
||||
/// Add effects for this light state.
|
||||
void add_effects(const std::vector<LightEffect *> &effects);
|
||||
|
||||
/// Get the total number of effects available for this light.
|
||||
size_t get_effect_count() const { return this->effects_.size(); }
|
||||
|
||||
/// Get the currently active effect index (0 = no effect, 1+ = effect index).
|
||||
uint32_t get_current_effect_index() const { return this->active_effect_index_; }
|
||||
|
||||
/// Get effect index by name. Returns 0 if effect not found.
|
||||
uint32_t get_effect_index(const std::string &effect_name) const {
|
||||
if (strcasecmp(effect_name.c_str(), "none") == 0) {
|
||||
return 0;
|
||||
}
|
||||
for (size_t i = 0; i < this->effects_.size(); i++) {
|
||||
if (strcasecmp(effect_name.c_str(), this->effects_[i]->get_name().c_str()) == 0) {
|
||||
return i + 1; // Effects are 1-indexed in active_effect_index_
|
||||
}
|
||||
}
|
||||
return 0; // Effect not found
|
||||
}
|
||||
|
||||
/// Get effect by index. Returns nullptr if index is invalid.
|
||||
LightEffect *get_effect_by_index(uint32_t index) const {
|
||||
if (index == 0 || index > this->effects_.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return this->effects_[index - 1]; // Effects are 1-indexed in active_effect_index_
|
||||
}
|
||||
|
||||
/// Get effect name by index. Returns "None" for index 0, empty string for invalid index.
|
||||
std::string get_effect_name_by_index(uint32_t index) const {
|
||||
if (index == 0) {
|
||||
return "None";
|
||||
}
|
||||
if (index > this->effects_.size()) {
|
||||
return ""; // Invalid index
|
||||
}
|
||||
return this->effects_[index - 1]->get_name();
|
||||
}
|
||||
|
||||
/// The result of all the current_values_as_* methods have gamma correction applied.
|
||||
void current_values_as_binary(bool *binary);
|
||||
|
||||
|
@@ -21,7 +21,7 @@ class LVGLNumber : public number::Number, public Component {
|
||||
void setup() override {
|
||||
float value = this->value_lambda_();
|
||||
if (this->restore_) {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
|
||||
if (this->pref_.load(&value)) {
|
||||
this->control_lambda_(value);
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ class LVGLSelect : public select::Select, public Component {
|
||||
this->set_options_();
|
||||
if (this->restore_) {
|
||||
size_t index;
|
||||
this->pref_ = global_preferences->make_preference<size_t>(this->get_preference_hash());
|
||||
this->pref_ = global_preferences->make_preference<size_t>(this->get_object_id_hash());
|
||||
if (this->pref_.load(&index))
|
||||
this->widget_->set_selected_index(index, LV_ANIM_OFF);
|
||||
}
|
||||
|
@@ -24,139 +24,100 @@ static const char *const TAG = "mdns";
|
||||
void MDNSComponent::compile_records_() {
|
||||
this->hostname_ = App.get_name();
|
||||
|
||||
// Calculate exact capacity needed for services vector
|
||||
size_t services_count = 0;
|
||||
this->services_.clear();
|
||||
#ifdef USE_API
|
||||
if (api::global_api_server != nullptr) {
|
||||
services_count++;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_PROMETHEUS
|
||||
services_count++;
|
||||
#endif
|
||||
#ifdef USE_WEBSERVER
|
||||
services_count++;
|
||||
#endif
|
||||
#ifdef USE_MDNS_EXTRA_SERVICES
|
||||
services_count += this->services_extra_.size();
|
||||
#endif
|
||||
// Reserve for fallback service if needed
|
||||
if (services_count == 0) {
|
||||
services_count = 1;
|
||||
}
|
||||
this->services_.reserve(services_count);
|
||||
|
||||
#ifdef USE_API
|
||||
if (api::global_api_server != nullptr) {
|
||||
this->services_.emplace_back();
|
||||
auto &service = this->services_.back();
|
||||
MDNSService service{};
|
||||
service.service_type = "_esphomelib";
|
||||
service.proto = "_tcp";
|
||||
service.port = api::global_api_server->get_port();
|
||||
|
||||
const std::string &friendly_name = App.get_friendly_name();
|
||||
bool friendly_name_empty = friendly_name.empty();
|
||||
|
||||
// Calculate exact capacity for txt_records
|
||||
size_t txt_count = 3; // version, mac, board (always present)
|
||||
if (!friendly_name_empty) {
|
||||
txt_count++; // friendly_name
|
||||
if (!App.get_friendly_name().empty()) {
|
||||
service.txt_records.push_back({"friendly_name", App.get_friendly_name()});
|
||||
}
|
||||
#if defined(USE_ESP8266) || defined(USE_ESP32) || defined(USE_RP2040) || defined(USE_LIBRETINY)
|
||||
txt_count++; // platform
|
||||
#endif
|
||||
#if defined(USE_WIFI) || defined(USE_ETHERNET) || defined(USE_OPENTHREAD)
|
||||
txt_count++; // network
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
txt_count++; // api_encryption or api_encryption_supported
|
||||
#endif
|
||||
#ifdef ESPHOME_PROJECT_NAME
|
||||
txt_count += 2; // project_name and project_version
|
||||
#endif
|
||||
#ifdef USE_DASHBOARD_IMPORT
|
||||
txt_count++; // package_import_url
|
||||
#endif
|
||||
|
||||
auto &txt_records = service.txt_records;
|
||||
txt_records.reserve(txt_count);
|
||||
|
||||
if (!friendly_name_empty) {
|
||||
txt_records.emplace_back(MDNSTXTRecord{"friendly_name", friendly_name});
|
||||
}
|
||||
txt_records.emplace_back(MDNSTXTRecord{"version", ESPHOME_VERSION});
|
||||
txt_records.emplace_back(MDNSTXTRecord{"mac", get_mac_address()});
|
||||
|
||||
service.txt_records.push_back({"version", ESPHOME_VERSION});
|
||||
service.txt_records.push_back({"mac", get_mac_address()});
|
||||
const char *platform = nullptr;
|
||||
#ifdef USE_ESP8266
|
||||
txt_records.emplace_back(MDNSTXTRecord{"platform", "ESP8266"});
|
||||
#elif defined(USE_ESP32)
|
||||
txt_records.emplace_back(MDNSTXTRecord{"platform", "ESP32"});
|
||||
#elif defined(USE_RP2040)
|
||||
txt_records.emplace_back(MDNSTXTRecord{"platform", "RP2040"});
|
||||
#elif defined(USE_LIBRETINY)
|
||||
txt_records.emplace_back(MDNSTXTRecord{"platform", lt_cpu_get_model_name()});
|
||||
platform = "ESP8266";
|
||||
#endif
|
||||
#ifdef USE_ESP32
|
||||
platform = "ESP32";
|
||||
#endif
|
||||
#ifdef USE_RP2040
|
||||
platform = "RP2040";
|
||||
#endif
|
||||
#ifdef USE_LIBRETINY
|
||||
platform = lt_cpu_get_model_name();
|
||||
#endif
|
||||
if (platform != nullptr) {
|
||||
service.txt_records.push_back({"platform", platform});
|
||||
}
|
||||
|
||||
txt_records.emplace_back(MDNSTXTRecord{"board", ESPHOME_BOARD});
|
||||
service.txt_records.push_back({"board", ESPHOME_BOARD});
|
||||
|
||||
#if defined(USE_WIFI)
|
||||
txt_records.emplace_back(MDNSTXTRecord{"network", "wifi"});
|
||||
service.txt_records.push_back({"network", "wifi"});
|
||||
#elif defined(USE_ETHERNET)
|
||||
txt_records.emplace_back(MDNSTXTRecord{"network", "ethernet"});
|
||||
service.txt_records.push_back({"network", "ethernet"});
|
||||
#elif defined(USE_OPENTHREAD)
|
||||
txt_records.emplace_back(MDNSTXTRecord{"network", "thread"});
|
||||
service.txt_records.push_back({"network", "thread"});
|
||||
#endif
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
static constexpr const char *NOISE_ENCRYPTION = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
|
||||
if (api::global_api_server->get_noise_ctx()->has_psk()) {
|
||||
txt_records.emplace_back(MDNSTXTRecord{"api_encryption", NOISE_ENCRYPTION});
|
||||
service.txt_records.push_back({"api_encryption", "Noise_NNpsk0_25519_ChaChaPoly_SHA256"});
|
||||
} else {
|
||||
txt_records.emplace_back(MDNSTXTRecord{"api_encryption_supported", NOISE_ENCRYPTION});
|
||||
service.txt_records.push_back({"api_encryption_supported", "Noise_NNpsk0_25519_ChaChaPoly_SHA256"});
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ESPHOME_PROJECT_NAME
|
||||
txt_records.emplace_back(MDNSTXTRecord{"project_name", ESPHOME_PROJECT_NAME});
|
||||
txt_records.emplace_back(MDNSTXTRecord{"project_version", ESPHOME_PROJECT_VERSION});
|
||||
service.txt_records.push_back({"project_name", ESPHOME_PROJECT_NAME});
|
||||
service.txt_records.push_back({"project_version", ESPHOME_PROJECT_VERSION});
|
||||
#endif // ESPHOME_PROJECT_NAME
|
||||
|
||||
#ifdef USE_DASHBOARD_IMPORT
|
||||
txt_records.emplace_back(MDNSTXTRecord{"package_import_url", dashboard_import::get_package_import_url()});
|
||||
service.txt_records.push_back({"package_import_url", dashboard_import::get_package_import_url()});
|
||||
#endif
|
||||
|
||||
this->services_.push_back(service);
|
||||
}
|
||||
#endif // USE_API
|
||||
|
||||
#ifdef USE_PROMETHEUS
|
||||
this->services_.emplace_back();
|
||||
auto &prom_service = this->services_.back();
|
||||
prom_service.service_type = "_prometheus-http";
|
||||
prom_service.proto = "_tcp";
|
||||
prom_service.port = USE_WEBSERVER_PORT;
|
||||
{
|
||||
MDNSService service{};
|
||||
service.service_type = "_prometheus-http";
|
||||
service.proto = "_tcp";
|
||||
service.port = USE_WEBSERVER_PORT;
|
||||
this->services_.push_back(service);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
this->services_.emplace_back();
|
||||
auto &web_service = this->services_.back();
|
||||
web_service.service_type = "_http";
|
||||
web_service.proto = "_tcp";
|
||||
web_service.port = USE_WEBSERVER_PORT;
|
||||
{
|
||||
MDNSService service{};
|
||||
service.service_type = "_http";
|
||||
service.proto = "_tcp";
|
||||
service.port = USE_WEBSERVER_PORT;
|
||||
this->services_.push_back(service);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_MDNS_EXTRA_SERVICES
|
||||
this->services_.insert(this->services_.end(), this->services_extra_.begin(), this->services_extra_.end());
|
||||
#endif
|
||||
|
||||
#if !defined(USE_API) && !defined(USE_PROMETHEUS) && !defined(USE_WEBSERVER) && !defined(USE_MDNS_EXTRA_SERVICES)
|
||||
// Publish "http" service if not using native API or any other services
|
||||
// This is just to have *some* mDNS service so that .local resolution works
|
||||
this->services_.emplace_back();
|
||||
auto &fallback_service = this->services_.back();
|
||||
fallback_service.service_type = "_http";
|
||||
fallback_service.proto = "_tcp";
|
||||
fallback_service.port = USE_WEBSERVER_PORT;
|
||||
fallback_service.txt_records.emplace_back(MDNSTXTRecord{"version", ESPHOME_VERSION});
|
||||
#endif
|
||||
if (this->services_.empty()) {
|
||||
// Publish "http" service if not using native API
|
||||
// This is just to have *some* mDNS service so that .local resolution works
|
||||
MDNSService service{};
|
||||
service.service_type = "_http";
|
||||
service.proto = "_tcp";
|
||||
service.port = USE_WEBSERVER_PORT;
|
||||
service.txt_records.push_back({"version", ESPHOME_VERSION});
|
||||
this->services_.push_back(service);
|
||||
}
|
||||
}
|
||||
|
||||
void MDNSComponent::dump_config() {
|
||||
@@ -164,7 +125,6 @@ void MDNSComponent::dump_config() {
|
||||
"mDNS:\n"
|
||||
" Hostname: %s",
|
||||
this->hostname_.c_str());
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
ESP_LOGV(TAG, " Services:");
|
||||
for (const auto &service : this->services_) {
|
||||
ESP_LOGV(TAG, " - %s, %s, %d", service.service_type.c_str(), service.proto.c_str(),
|
||||
@@ -174,7 +134,6 @@ void MDNSComponent::dump_config() {
|
||||
const_cast<TemplatableValue<std::string> &>(record.value).value().c_str());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<MDNSService> MDNSComponent::get_services() { return this->services_; }
|
||||
|
@@ -50,13 +50,28 @@ bool MLX90614Component::write_emissivity_() {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t MLX90614Component::crc8_pec_(const uint8_t *data, uint8_t len) {
|
||||
uint8_t crc = 0;
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
uint8_t in = data[i];
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
bool carry = (crc ^ in) & 0x80;
|
||||
crc <<= 1;
|
||||
if (carry)
|
||||
crc ^= 0x07;
|
||||
in <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool MLX90614Component::write_bytes_(uint8_t reg, uint16_t data) {
|
||||
uint8_t buf[5];
|
||||
buf[0] = this->address_ << 1;
|
||||
buf[1] = reg;
|
||||
buf[2] = data & 0xFF;
|
||||
buf[3] = data >> 8;
|
||||
buf[4] = crc8(buf, 4, 0x00, 0x07, true);
|
||||
buf[4] = this->crc8_pec_(buf, 4);
|
||||
return this->write_bytes(reg, buf + 2, 3);
|
||||
}
|
||||
|
||||
|
@@ -22,6 +22,7 @@ class MLX90614Component : public PollingComponent, public i2c::I2CDevice {
|
||||
protected:
|
||||
bool write_emissivity_();
|
||||
|
||||
uint8_t crc8_pec_(const uint8_t *data, uint8_t len);
|
||||
bool write_bytes_(uint8_t reg, uint16_t data);
|
||||
|
||||
sensor::Sensor *ambient_sensor_{nullptr};
|
||||
|
@@ -764,8 +764,7 @@ void Nextion::process_nextion_commands_() {
|
||||
variable_name = to_process.substr(0, index);
|
||||
++index;
|
||||
|
||||
// Get variable value without terminating NUL byte. Length check above ensures substr len >= 0.
|
||||
text_value = to_process.substr(index, to_process_length - index - 1);
|
||||
text_value = to_process.substr(index);
|
||||
|
||||
ESP_LOGN(TAG, "Text sensor: %s='%s'", variable_name.c_str(), text_value.c_str());
|
||||
|
||||
|
@@ -11,7 +11,7 @@ void NTC::setup() {
|
||||
if (this->sensor_->has_state())
|
||||
this->process_(this->sensor_->state);
|
||||
}
|
||||
void NTC::dump_config() { LOG_SENSOR("", "NTC Sensor", this); }
|
||||
void NTC::dump_config() { LOG_SENSOR("", "NTC Sensor", this) }
|
||||
float NTC::get_setup_priority() const { return setup_priority::DATA; }
|
||||
void NTC::process_(float value) {
|
||||
if (std::isnan(value)) {
|
||||
|
@@ -15,7 +15,7 @@ void ValueRangeTrigger::setup() {
|
||||
float local_min = this->min_.value(0.0);
|
||||
float local_max = this->max_.value(0.0);
|
||||
convert hash = {.from = (local_max - local_min)};
|
||||
uint32_t myhash = hash.to ^ this->parent_->get_preference_hash();
|
||||
uint32_t myhash = hash.to ^ this->parent_->get_object_id_hash();
|
||||
this->rtc_ = global_preferences->make_preference<bool>(myhash);
|
||||
bool initial_state;
|
||||
if (this->rtc_.load(&initial_state)) {
|
||||
|
@@ -6,27 +6,6 @@ namespace number {
|
||||
|
||||
static const char *const TAG = "number";
|
||||
|
||||
// Function implementation of LOG_NUMBER macro to reduce code size
|
||||
void log_number(const char *tag, const char *prefix, const char *type, Number *obj) {
|
||||
if (obj == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
|
||||
|
||||
if (!obj->get_icon().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon().c_str());
|
||||
}
|
||||
|
||||
if (!obj->traits.get_unit_of_measurement().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Unit of Measurement: '%s'", prefix, obj->traits.get_unit_of_measurement().c_str());
|
||||
}
|
||||
|
||||
if (!obj->traits.get_device_class().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->traits.get_device_class().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Number::publish_state(float state) {
|
||||
this->set_has_state(true);
|
||||
this->state = state;
|
||||
|
@@ -9,10 +9,19 @@
|
||||
namespace esphome {
|
||||
namespace number {
|
||||
|
||||
class Number;
|
||||
void log_number(const char *tag, const char *prefix, const char *type, Number *obj);
|
||||
|
||||
#define LOG_NUMBER(prefix, type, obj) log_number(TAG, prefix, LOG_STR_LITERAL(type), obj)
|
||||
#define LOG_NUMBER(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
|
||||
} \
|
||||
if (!(obj)->traits.get_unit_of_measurement().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->traits.get_unit_of_measurement().c_str()); \
|
||||
} \
|
||||
if (!(obj)->traits.get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->traits.get_device_class().c_str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SUB_NUMBER(name) \
|
||||
protected: \
|
||||
|
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <vector>
|
||||
|
||||
#include "opentherm.h"
|
||||
|
||||
@@ -17,21 +17,21 @@
|
||||
#endif
|
||||
|
||||
#ifdef OPENTHERM_USE_SWITCH
|
||||
#include "esphome/components/opentherm/switch/opentherm_switch.h"
|
||||
#include "esphome/components/opentherm/switch/switch.h"
|
||||
#endif
|
||||
|
||||
#ifdef OPENTHERM_USE_OUTPUT
|
||||
#include "esphome/components/opentherm/output/opentherm_output.h"
|
||||
#include "esphome/components/opentherm/output/output.h"
|
||||
#endif
|
||||
|
||||
#ifdef OPENTHERM_USE_NUMBER
|
||||
#include "esphome/components/opentherm/number/opentherm_number.h"
|
||||
#include "esphome/components/opentherm/number/number.h"
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <functional>
|
||||
|
||||
#include "opentherm_macros.h"
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "opentherm_number.h"
|
||||
#include "number.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace opentherm {
|
||||
@@ -17,7 +17,7 @@ void OpenthermNumber::setup() {
|
||||
if (!this->restore_value_) {
|
||||
value = this->initial_value_;
|
||||
} else {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
|
||||
if (!this->pref_.load(&value)) {
|
||||
if (!std::isnan(this->initial_value_)) {
|
||||
value = this->initial_value_;
|
@@ -1,5 +1,5 @@
|
||||
#include "esphome/core/helpers.h" // for clamp() and lerp()
|
||||
#include "opentherm_output.h"
|
||||
#include "output.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace opentherm {
|
@@ -1,4 +1,4 @@
|
||||
#include "opentherm_switch.h"
|
||||
#include "switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace opentherm {
|
@@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
yield cg.register_component(var, config)
|
||||
yield uart.register_uart_device(var, config)
|
||||
|
@@ -99,9 +99,9 @@ async def to_code(config):
|
||||
}
|
||||
),
|
||||
)
|
||||
async def output_pipsolar_set_level_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
def output_pipsolar_set_level_to_code(config, action_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(config[CONF_VALUE], args, float)
|
||||
template_ = yield cg.templatable(config[CONF_VALUE], args, float)
|
||||
cg.add(var.set_level(template_))
|
||||
return var
|
||||
yield var
|
||||
|
@@ -23,18 +23,20 @@ void Pipsolar::loop() {
|
||||
// Read message
|
||||
if (this->state_ == STATE_IDLE) {
|
||||
this->empty_uart_buffer_();
|
||||
|
||||
if (this->send_next_command_()) {
|
||||
// command sent
|
||||
return;
|
||||
switch (this->send_next_command_()) {
|
||||
case 0:
|
||||
// no command send (empty queue) time to poll
|
||||
if (millis() - this->last_poll_ > this->update_interval_) {
|
||||
this->send_next_poll_();
|
||||
this->last_poll_ = millis();
|
||||
}
|
||||
return;
|
||||
break;
|
||||
case 1:
|
||||
// command send
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->send_next_poll_()) {
|
||||
// poll sent
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (this->state_ == STATE_COMMAND_COMPLETE) {
|
||||
if (this->check_incoming_length_(4)) {
|
||||
@@ -528,7 +530,7 @@ void Pipsolar::loop() {
|
||||
// '(00000000000000000000000000000000'
|
||||
// iterate over all available flag (as not all models have all flags, but at least in the same order)
|
||||
this->value_warnings_present_ = false;
|
||||
this->value_faults_present_ = false;
|
||||
this->value_faults_present_ = true;
|
||||
|
||||
for (size_t i = 1; i < strlen(tmp); i++) {
|
||||
enabled = tmp[i] == '1';
|
||||
@@ -706,7 +708,6 @@ void Pipsolar::loop() {
|
||||
return;
|
||||
}
|
||||
// crc ok
|
||||
this->used_polling_commands_[this->last_polling_command_].needs_update = false;
|
||||
this->state_ = STATE_POLL_CHECKED;
|
||||
return;
|
||||
} else {
|
||||
@@ -787,7 +788,7 @@ uint8_t Pipsolar::check_incoming_crc_() {
|
||||
}
|
||||
|
||||
// send next command used
|
||||
bool Pipsolar::send_next_command_() {
|
||||
uint8_t Pipsolar::send_next_command_() {
|
||||
uint16_t crc16;
|
||||
if (!this->command_queue_[this->command_queue_position_].empty()) {
|
||||
const char *command = this->command_queue_[this->command_queue_position_].c_str();
|
||||
@@ -808,43 +809,37 @@ bool Pipsolar::send_next_command_() {
|
||||
// end Byte
|
||||
this->write(0x0D);
|
||||
ESP_LOGD(TAG, "Sending command from queue: %s with length %d", command, length);
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Pipsolar::send_next_poll_() {
|
||||
void Pipsolar::send_next_poll_() {
|
||||
uint16_t crc16;
|
||||
|
||||
for (uint8_t i = 0; i < POLLING_COMMANDS_MAX; i++) {
|
||||
this->last_polling_command_ = (this->last_polling_command_ + 1) % POLLING_COMMANDS_MAX;
|
||||
if (this->used_polling_commands_[this->last_polling_command_].length == 0) {
|
||||
// not enabled
|
||||
continue;
|
||||
}
|
||||
if (!this->used_polling_commands_[this->last_polling_command_].needs_update) {
|
||||
// no update requested
|
||||
continue;
|
||||
}
|
||||
this->state_ = STATE_POLL;
|
||||
this->command_start_millis_ = millis();
|
||||
this->empty_uart_buffer_();
|
||||
this->read_pos_ = 0;
|
||||
crc16 = this->pipsolar_crc_(this->used_polling_commands_[this->last_polling_command_].command,
|
||||
this->used_polling_commands_[this->last_polling_command_].length);
|
||||
this->write_array(this->used_polling_commands_[this->last_polling_command_].command,
|
||||
this->used_polling_commands_[this->last_polling_command_].length);
|
||||
// checksum
|
||||
this->write(((uint8_t) ((crc16) >> 8))); // highbyte
|
||||
this->write(((uint8_t) ((crc16) &0xff))); // lowbyte
|
||||
// end Byte
|
||||
this->write(0x0D);
|
||||
ESP_LOGD(TAG, "Sending polling command : %s with length %d",
|
||||
this->used_polling_commands_[this->last_polling_command_].command,
|
||||
this->used_polling_commands_[this->last_polling_command_].length);
|
||||
return true;
|
||||
this->last_polling_command_ = (this->last_polling_command_ + 1) % 15;
|
||||
if (this->used_polling_commands_[this->last_polling_command_].length == 0) {
|
||||
this->last_polling_command_ = 0;
|
||||
}
|
||||
return false;
|
||||
if (this->used_polling_commands_[this->last_polling_command_].length == 0) {
|
||||
// no command specified
|
||||
return;
|
||||
}
|
||||
this->state_ = STATE_POLL;
|
||||
this->command_start_millis_ = millis();
|
||||
this->empty_uart_buffer_();
|
||||
this->read_pos_ = 0;
|
||||
crc16 = this->pipsolar_crc_(this->used_polling_commands_[this->last_polling_command_].command,
|
||||
this->used_polling_commands_[this->last_polling_command_].length);
|
||||
this->write_array(this->used_polling_commands_[this->last_polling_command_].command,
|
||||
this->used_polling_commands_[this->last_polling_command_].length);
|
||||
// checksum
|
||||
this->write(((uint8_t) ((crc16) >> 8))); // highbyte
|
||||
this->write(((uint8_t) ((crc16) &0xff))); // lowbyte
|
||||
// end Byte
|
||||
this->write(0x0D);
|
||||
ESP_LOGD(TAG, "Sending polling command : %s with length %d",
|
||||
this->used_polling_commands_[this->last_polling_command_].command,
|
||||
this->used_polling_commands_[this->last_polling_command_].length);
|
||||
}
|
||||
|
||||
void Pipsolar::queue_command_(const char *command, uint8_t length) {
|
||||
@@ -874,13 +869,7 @@ void Pipsolar::dump_config() {
|
||||
}
|
||||
}
|
||||
}
|
||||
void Pipsolar::update() {
|
||||
for (auto &used_polling_command : this->used_polling_commands_) {
|
||||
if (used_polling_command.length != 0) {
|
||||
used_polling_command.needs_update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Pipsolar::update() {}
|
||||
|
||||
void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand polling_command) {
|
||||
for (auto &used_polling_command : this->used_polling_commands_) {
|
||||
@@ -902,7 +891,6 @@ void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand poll
|
||||
used_polling_command.errors = 0;
|
||||
used_polling_command.identifier = polling_command;
|
||||
used_polling_command.length = length - 1;
|
||||
used_polling_command.needs_update = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -25,7 +25,6 @@ struct PollingCommand {
|
||||
uint8_t length = 0;
|
||||
uint8_t errors;
|
||||
ENUMPollingCommand identifier;
|
||||
bool needs_update;
|
||||
};
|
||||
|
||||
#define PIPSOLAR_VALUED_ENTITY_(type, name, polling_command, value_type) \
|
||||
@@ -190,14 +189,14 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent {
|
||||
static const size_t PIPSOLAR_READ_BUFFER_LENGTH = 110; // maximum supported answer length
|
||||
static const size_t COMMAND_QUEUE_LENGTH = 10;
|
||||
static const size_t COMMAND_TIMEOUT = 5000;
|
||||
static const size_t POLLING_COMMANDS_MAX = 15;
|
||||
uint32_t last_poll_ = 0;
|
||||
void add_polling_command_(const char *command, ENUMPollingCommand polling_command);
|
||||
void empty_uart_buffer_();
|
||||
uint8_t check_incoming_crc_();
|
||||
uint8_t check_incoming_length_(uint8_t length);
|
||||
uint16_t pipsolar_crc_(uint8_t *msg, uint8_t len);
|
||||
bool send_next_command_();
|
||||
bool send_next_poll_();
|
||||
uint8_t send_next_command_();
|
||||
void send_next_poll_();
|
||||
void queue_command_(const char *command, uint8_t length);
|
||||
std::string command_queue_[COMMAND_QUEUE_LENGTH];
|
||||
uint8_t command_queue_position_ = 0;
|
||||
@@ -217,7 +216,7 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent {
|
||||
};
|
||||
|
||||
uint8_t last_polling_command_ = 0;
|
||||
PollingCommand used_polling_commands_[POLLING_COMMANDS_MAX];
|
||||
PollingCommand used_polling_commands_[15];
|
||||
};
|
||||
|
||||
} // namespace pipsolar
|
||||
|
@@ -17,7 +17,7 @@ void IRAM_ATTR PulseWidthSensorStore::gpio_intr(PulseWidthSensorStore *arg) {
|
||||
}
|
||||
|
||||
void PulseWidthSensor::dump_config() {
|
||||
LOG_SENSOR("", "Pulse Width", this);
|
||||
LOG_SENSOR("", "Pulse Width", this)
|
||||
LOG_UPDATE_INTERVAL(this)
|
||||
LOG_PIN(" Pin: ", this->pin_);
|
||||
}
|
||||
|
@@ -46,32 +46,10 @@ void PVVXDisplay::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t
|
||||
}
|
||||
this->connection_established_ = true;
|
||||
this->char_handle_ = chr->handle;
|
||||
|
||||
// Attempt to write immediately
|
||||
// For devices without security, this will work
|
||||
// For devices with security that are already paired, this will work
|
||||
// For devices that need pairing, the write will be retried after auth completes
|
||||
this->sync_time_and_display_();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PVVXDisplay::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT: {
|
||||
if (!this->parent_->check_addr(param->ble_security.auth_cmpl.bd_addr))
|
||||
return;
|
||||
|
||||
if (param->ble_security.auth_cmpl.success) {
|
||||
ESP_LOGD(TAG, "[%s] Authentication successful, performing writes.", this->parent_->address_str().c_str());
|
||||
// Now that pairing is complete, perform the pending writes
|
||||
this->sync_time_and_display_();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "[%s] Authentication failed.", this->parent_->address_str().c_str());
|
||||
}
|
||||
#ifdef USE_TIME
|
||||
this->sync_time_();
|
||||
#endif
|
||||
this->display();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -149,13 +127,6 @@ void PVVXDisplay::delayed_disconnect_() {
|
||||
this->set_timeout("disconnect", this->disconnect_delay_ms_, [this]() { this->parent_->set_enabled(false); });
|
||||
}
|
||||
|
||||
void PVVXDisplay::sync_time_and_display_() {
|
||||
#ifdef USE_TIME
|
||||
this->sync_time_();
|
||||
#endif
|
||||
this->display();
|
||||
}
|
||||
|
||||
#ifdef USE_TIME
|
||||
void PVVXDisplay::sync_time_() {
|
||||
if (this->time_ == nullptr)
|
||||
|
@@ -43,7 +43,6 @@ class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent {
|
||||
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
|
||||
|
||||
/// Set validity period of the display information in seconds (1..65535)
|
||||
void set_validity_period(uint16_t validity_period) { this->validity_period_ = validity_period; }
|
||||
@@ -113,7 +112,6 @@ class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent {
|
||||
void setcfgbit_(uint8_t bit, bool value);
|
||||
void send_to_setup_char_(uint8_t *blk, size_t size);
|
||||
void delayed_disconnect_();
|
||||
void sync_time_and_display_();
|
||||
#ifdef USE_TIME
|
||||
void sync_time_();
|
||||
time::RealTimeClock *time_{nullptr};
|
||||
|
@@ -18,6 +18,6 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||
|
@@ -1782,12 +1782,14 @@ def nexa_dumper(var, config):
|
||||
|
||||
|
||||
@register_action("nexa", NexaAction, NEXA_SCHEMA)
|
||||
async def nexa_action(var, config, args):
|
||||
cg.add(var.set_device(await cg.templatable(config[CONF_DEVICE], args, cg.uint32)))
|
||||
cg.add(var.set_group(await cg.templatable(config[CONF_GROUP], args, cg.uint8)))
|
||||
cg.add(var.set_state(await cg.templatable(config[CONF_STATE], args, cg.uint8)))
|
||||
cg.add(var.set_channel(await cg.templatable(config[CONF_CHANNEL], args, cg.uint8)))
|
||||
cg.add(var.set_level(await cg.templatable(config[CONF_LEVEL], args, cg.uint8)))
|
||||
def nexa_action(var, config, args):
|
||||
cg.add(var.set_device((yield cg.templatable(config[CONF_DEVICE], args, cg.uint32))))
|
||||
cg.add(var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.uint8))))
|
||||
cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, cg.uint8))))
|
||||
cg.add(
|
||||
var.set_channel((yield cg.templatable(config[CONF_CHANNEL], args, cg.uint8)))
|
||||
)
|
||||
cg.add(var.set_level((yield cg.templatable(config[CONF_LEVEL], args, cg.uint8))))
|
||||
|
||||
|
||||
# Midea
|
||||
|
@@ -132,7 +132,7 @@ void RotaryEncoderSensor::setup() {
|
||||
int32_t initial_value = 0;
|
||||
switch (this->restore_mode_) {
|
||||
case ROTARY_ENCODER_RESTORE_DEFAULT_ZERO:
|
||||
this->rtc_ = global_preferences->make_preference<int32_t>(this->get_preference_hash());
|
||||
this->rtc_ = global_preferences->make_preference<int32_t>(this->get_object_id_hash());
|
||||
if (!this->rtc_.load(&initial_value)) {
|
||||
initial_value = 0;
|
||||
}
|
||||
|
@@ -15,11 +15,11 @@ namespace safe_mode {
|
||||
static const char *const TAG = "safe_mode";
|
||||
|
||||
void SafeModeComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Safe Mode:");
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"Safe Mode:\n"
|
||||
" Successful after: %" PRIu32 "s\n"
|
||||
" Invoke after: %u attempts\n"
|
||||
" Duration: %" PRIu32 "s",
|
||||
" Boot considered successful after %" PRIu32 " seconds\n"
|
||||
" Invoke after %u boot attempts\n"
|
||||
" Remain for %" PRIu32 " seconds",
|
||||
this->safe_mode_boot_is_good_after_ / 1000, // because milliseconds
|
||||
this->safe_mode_num_attempts_,
|
||||
this->safe_mode_enable_time_ / 1000); // because milliseconds
|
||||
@@ -27,7 +27,7 @@ void SafeModeComponent::dump_config() {
|
||||
if (this->safe_mode_rtc_value_ > 1 && this->safe_mode_rtc_value_ != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) {
|
||||
auto remaining_restarts = this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_;
|
||||
if (remaining_restarts) {
|
||||
ESP_LOGW(TAG, "Last reset too quick; invoke in %" PRIu32 " restarts", remaining_restarts);
|
||||
ESP_LOGW(TAG, "Last reset occurred too quickly; will be invoked in %" PRIu32 " restarts", remaining_restarts);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "SAFE MODE IS ACTIVE");
|
||||
}
|
||||
@@ -72,45 +72,43 @@ bool SafeModeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t en
|
||||
this->safe_mode_boot_is_good_after_ = boot_is_good_after;
|
||||
this->safe_mode_num_attempts_ = num_attempts;
|
||||
this->rtc_ = global_preferences->make_preference<uint32_t>(233825507UL, false);
|
||||
this->safe_mode_rtc_value_ = this->read_rtc_();
|
||||
|
||||
uint32_t rtc_val = this->read_rtc_();
|
||||
this->safe_mode_rtc_value_ = rtc_val;
|
||||
bool is_manual_safe_mode = this->safe_mode_rtc_value_ == SafeModeComponent::ENTER_SAFE_MODE_MAGIC;
|
||||
|
||||
bool is_manual = rtc_val == SafeModeComponent::ENTER_SAFE_MODE_MAGIC;
|
||||
|
||||
if (is_manual) {
|
||||
ESP_LOGI(TAG, "Manual mode");
|
||||
if (is_manual_safe_mode) {
|
||||
ESP_LOGI(TAG, "Safe mode invoked manually");
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, "Unsuccessful boot attempts: %" PRIu32, rtc_val);
|
||||
ESP_LOGCONFIG(TAG, "There have been %" PRIu32 " suspected unsuccessful boot attempts", this->safe_mode_rtc_value_);
|
||||
}
|
||||
|
||||
if (rtc_val < num_attempts && !is_manual) {
|
||||
if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) {
|
||||
this->clean_rtc();
|
||||
|
||||
if (!is_manual_safe_mode) {
|
||||
ESP_LOGE(TAG, "Boot loop detected. Proceeding");
|
||||
}
|
||||
|
||||
this->status_set_error();
|
||||
this->set_timeout(enable_time, []() {
|
||||
ESP_LOGW(TAG, "Safe mode enable time has elapsed -- restarting");
|
||||
App.reboot();
|
||||
});
|
||||
|
||||
// Delay here to allow power to stabilize before Wi-Fi/Ethernet is initialised
|
||||
delay(300); // NOLINT
|
||||
App.setup();
|
||||
|
||||
ESP_LOGW(TAG, "SAFE MODE IS ACTIVE");
|
||||
|
||||
this->safe_mode_callback_.call();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// increment counter
|
||||
this->write_rtc_(rtc_val + 1);
|
||||
this->write_rtc_(this->safe_mode_rtc_value_ + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->clean_rtc();
|
||||
|
||||
if (!is_manual) {
|
||||
ESP_LOGE(TAG, "Boot loop detected");
|
||||
}
|
||||
|
||||
this->status_set_error();
|
||||
this->set_timeout(enable_time, []() {
|
||||
ESP_LOGW(TAG, "Timeout, restarting");
|
||||
App.reboot();
|
||||
});
|
||||
|
||||
// Delay here to allow power to stabilize before Wi-Fi/Ethernet is initialised
|
||||
delay(300); // NOLINT
|
||||
App.setup();
|
||||
|
||||
ESP_LOGW(TAG, "SAFE MODE IS ACTIVE");
|
||||
|
||||
this->safe_mode_callback_.call();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SafeModeComponent::write_rtc_(uint32_t val) {
|
||||
|
@@ -53,14 +53,10 @@ void SenseAirComponent::update() {
|
||||
|
||||
this->status_clear_warning();
|
||||
const uint8_t length = response[2];
|
||||
const uint16_t status = encode_uint16(response[3], response[4]);
|
||||
const uint16_t ppm = encode_uint16(response[length + 1], response[length + 2]);
|
||||
const uint16_t status = (uint16_t(response[3]) << 8) | response[4];
|
||||
const int16_t ppm = int16_t((response[length + 1] << 8) | response[length + 2]);
|
||||
|
||||
ESP_LOGD(TAG, "SenseAir Received CO₂=%uppm Status=0x%02X", ppm, status);
|
||||
if (ppm == 0 && (status & SenseAirStatus::OUT_OF_RANGE_ERROR) != 0) {
|
||||
ESP_LOGD(TAG, "Discarding 0 ppm reading with out-of-range status.");
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "SenseAir Received CO₂=%dppm Status=0x%02X", ppm, status);
|
||||
if (this->co2_sensor_ != nullptr)
|
||||
this->co2_sensor_->publish_state(ppm);
|
||||
}
|
||||
|
@@ -8,17 +8,6 @@
|
||||
namespace esphome {
|
||||
namespace senseair {
|
||||
|
||||
enum SenseAirStatus : uint8_t {
|
||||
FATAL_ERROR = 1 << 0,
|
||||
OFFSET_ERROR = 1 << 1,
|
||||
ALGORITHM_ERROR = 1 << 2,
|
||||
OUTPUT_ERROR = 1 << 3,
|
||||
SELF_DIAGNOSTIC_ERROR = 1 << 4,
|
||||
OUT_OF_RANGE_ERROR = 1 << 5,
|
||||
MEMORY_ERROR = 1 << 6,
|
||||
RESERVED = 1 << 7
|
||||
};
|
||||
|
||||
class SenseAirComponent : public PollingComponent, public uart::UARTDevice {
|
||||
public:
|
||||
void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; }
|
||||
|
@@ -40,7 +40,7 @@ class ValueRangeTrigger : public Trigger<float>, public Component {
|
||||
template<typename V> void set_max(V max) { this->max_ = max; }
|
||||
|
||||
void setup() override {
|
||||
this->rtc_ = global_preferences->make_preference<bool>(this->parent_->get_preference_hash());
|
||||
this->rtc_ = global_preferences->make_preference<bool>(this->parent_->get_object_id_hash());
|
||||
bool initial_state;
|
||||
if (this->rtc_.load(&initial_state)) {
|
||||
this->previous_in_range_ = initial_state;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user