mirror of
https://github.com/esphome/esphome.git
synced 2026-10-25 23:42:15 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34ecbd5d85 | ||
|
|
b66091622f | ||
|
|
f4c794e7ec | ||
|
|
f0473e7419 | ||
|
|
3325de5e28 | ||
|
|
afffe3ba36 | ||
|
|
6bf3e07a74 | ||
|
|
c6c1fce2bf | ||
|
|
4293d12008 | ||
|
|
51819d40cb | ||
|
|
e7ad8f9c80 |
No files matched your search
@@ -15,32 +15,25 @@ ESPHOME_LOG_TAG(TAG, "http_request.ota");
|
||||
|
||||
void OtaHttpRequestComponent::dump_config() { ESP_LOGCONFIG(TAG, "Over-The-Air updates via HTTP request"); };
|
||||
|
||||
OtaHttpRequestComponent::Request &OtaHttpRequestComponent::get_request_() {
|
||||
if (this->request_ == nullptr)
|
||||
this->request_ = make_unique<Request>();
|
||||
return *this->request_;
|
||||
}
|
||||
|
||||
void OtaHttpRequestComponent::set_md5_url(const std::string &url) {
|
||||
if (!this->validate_url_(url)) {
|
||||
this->get_request_().md5_url.clear(); // URL was not valid; prevent flashing until it is
|
||||
this->md5_url_.clear(); // URL was not valid; prevent flashing until it is
|
||||
return;
|
||||
}
|
||||
Request &request = this->get_request_();
|
||||
request.md5_url = url;
|
||||
request.md5_expected.clear(); // to be retrieved later
|
||||
this->md5_url_ = url;
|
||||
this->md5_expected_.clear(); // to be retrieved later
|
||||
}
|
||||
|
||||
void OtaHttpRequestComponent::set_url(const std::string &url) {
|
||||
if (!this->validate_url_(url)) {
|
||||
this->get_request_().url.clear(); // URL was not valid; prevent flashing until it is
|
||||
this->url_.clear(); // URL was not valid; prevent flashing until it is
|
||||
return;
|
||||
}
|
||||
this->get_request_().url = url;
|
||||
this->url_ = url;
|
||||
}
|
||||
|
||||
void OtaHttpRequestComponent::flash() {
|
||||
if (this->request_ == nullptr || this->request_->url.empty()) {
|
||||
if (this->url_.empty()) {
|
||||
ESP_LOGE(TAG, "URL not set; cannot start update");
|
||||
return;
|
||||
}
|
||||
@@ -65,8 +58,8 @@ void OtaHttpRequestComponent::flash() {
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_ERROR, 0.0f, ota_status);
|
||||
#endif
|
||||
this->request_->md5_computed.clear(); // will be reset at next attempt
|
||||
this->request_->md5_expected.clear(); // will be reset at next attempt
|
||||
this->md5_computed_.clear(); // will be reset at next attempt
|
||||
this->md5_expected_.clear(); // will be reset at next attempt
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -88,18 +81,18 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
||||
md5::MD5Digest md5_receive;
|
||||
char md5_receive_str[33];
|
||||
|
||||
if (this->request_->md5_expected.empty() && !this->http_get_md5_()) {
|
||||
if (this->md5_expected_.empty() && !this->http_get_md5_()) {
|
||||
return OTA_MD5_INVALID;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "MD5 expected: %s", this->request_->md5_expected.c_str());
|
||||
ESP_LOGD(TAG, "MD5 expected: %s", this->md5_expected_.c_str());
|
||||
|
||||
auto url_with_auth = this->get_url_with_auth_(this->request_->url);
|
||||
auto url_with_auth = this->get_url_with_auth_(this->url_);
|
||||
if (url_with_auth.empty()) {
|
||||
return OTA_BAD_URL;
|
||||
}
|
||||
ESP_LOGVV(TAG, "url_with_auth: %s", url_with_auth.c_str());
|
||||
ESP_LOGI(TAG, "Connecting to: %s", this->request_->url.c_str());
|
||||
ESP_LOGI(TAG, "Connecting to: %s", this->url_.c_str());
|
||||
|
||||
auto container = this->parent_->get(url_with_auth);
|
||||
|
||||
@@ -186,9 +179,9 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
||||
// verify MD5 is as expected and act accordingly
|
||||
md5_receive.calculate();
|
||||
md5_receive.get_hex(md5_receive_str);
|
||||
this->request_->md5_computed = md5_receive_str;
|
||||
if (strncmp(this->request_->md5_computed.c_str(), this->request_->md5_expected.c_str(), MD5_SIZE) != 0) {
|
||||
ESP_LOGE(TAG, "MD5 computed: %s - Aborting due to MD5 mismatch", this->request_->md5_computed.c_str());
|
||||
this->md5_computed_ = md5_receive_str;
|
||||
if (strncmp(this->md5_computed_.c_str(), this->md5_expected_.c_str(), MD5_SIZE) != 0) {
|
||||
ESP_LOGE(TAG, "MD5 computed: %s - Aborting due to MD5 mismatch", this->md5_computed_.c_str());
|
||||
this->cleanup_(std::move(backend), container, /*abort_backend=*/true);
|
||||
return ota::OTA_RESPONSE_ERROR_MD5_MISMATCH;
|
||||
} else {
|
||||
@@ -230,15 +223,11 @@ static std::string url_encode(const std::string &str) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void OtaHttpRequestComponent::set_password(const std::string &password) {
|
||||
this->get_request_().password = url_encode(password);
|
||||
}
|
||||
void OtaHttpRequestComponent::set_username(const std::string &username) {
|
||||
this->get_request_().username = url_encode(username);
|
||||
}
|
||||
void OtaHttpRequestComponent::set_password(const std::string &password) { this->password_ = url_encode(password); }
|
||||
void OtaHttpRequestComponent::set_username(const std::string &username) { this->username_ = url_encode(username); }
|
||||
|
||||
std::string OtaHttpRequestComponent::get_url_with_auth_(const std::string &url) {
|
||||
if (this->request_->username.empty() || this->request_->password.empty()) {
|
||||
if (this->username_.empty() || this->password_.empty()) {
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -251,23 +240,23 @@ std::string OtaHttpRequestComponent::get_url_with_auth_(const std::string &url)
|
||||
ESP_LOGD(TAG, "Using basic HTTP authentication");
|
||||
|
||||
start_char += 3; // skip '://' characters
|
||||
auto url_with_auth = url.substr(0, start_char) + this->request_->username + ":" + this->request_->password + "@" +
|
||||
url.substr(start_char);
|
||||
auto url_with_auth =
|
||||
url.substr(0, start_char) + this->username_ + ":" + this->password_ + "@" + url.substr(start_char);
|
||||
return url_with_auth;
|
||||
}
|
||||
|
||||
bool OtaHttpRequestComponent::http_get_md5_() {
|
||||
if (this->request_->md5_url.empty()) {
|
||||
if (this->md5_url_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto url_with_auth = this->get_url_with_auth_(this->request_->md5_url);
|
||||
auto url_with_auth = this->get_url_with_auth_(this->md5_url_);
|
||||
if (url_with_auth.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "url_with_auth: %s", url_with_auth.c_str());
|
||||
ESP_LOGI(TAG, "Connecting to: %s", this->request_->md5_url.c_str());
|
||||
ESP_LOGI(TAG, "Connecting to: %s", this->md5_url_.c_str());
|
||||
auto container = this->parent_->get(url_with_auth);
|
||||
if (container == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to connect to MD5 URL");
|
||||
@@ -284,8 +273,8 @@ bool OtaHttpRequestComponent::http_get_md5_() {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->request_->md5_expected.resize(MD5_SIZE);
|
||||
auto result = http_read_fully(container.get(), (uint8_t *) this->request_->md5_expected.data(), MD5_SIZE, MD5_SIZE,
|
||||
this->md5_expected_.resize(MD5_SIZE);
|
||||
auto result = http_read_fully(container.get(), (uint8_t *) this->md5_expected_.data(), MD5_SIZE, MD5_SIZE,
|
||||
this->parent_->get_timeout());
|
||||
container->end();
|
||||
|
||||
|
||||
@@ -27,35 +27,29 @@ class OtaHttpRequestComponent final : public ota::OTAComponent, public Parented<
|
||||
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||
|
||||
void set_md5_url(const std::string &md5_url);
|
||||
void set_md5(const std::string &md5) { this->get_request_().md5_expected = md5; }
|
||||
void set_md5(const std::string &md5) { this->md5_expected_ = md5; }
|
||||
void set_password(const std::string &password);
|
||||
void set_url(const std::string &url);
|
||||
void set_username(const std::string &username);
|
||||
|
||||
std::string md5_computed() { return this->request_ != nullptr ? this->request_->md5_computed : std::string(); }
|
||||
std::string md5_expected() { return this->request_ != nullptr ? this->request_->md5_expected : std::string(); }
|
||||
std::string md5_computed() { return this->md5_computed_; }
|
||||
std::string md5_expected() { return this->md5_expected_; }
|
||||
|
||||
void flash();
|
||||
|
||||
protected:
|
||||
/// Allocated by the first setter, which only runs before a flash, and never freed, so a flash never loses it.
|
||||
struct Request {
|
||||
std::string md5_computed;
|
||||
std::string md5_expected;
|
||||
std::string md5_url;
|
||||
std::string password;
|
||||
std::string username;
|
||||
std::string url;
|
||||
};
|
||||
Request &get_request_();
|
||||
|
||||
void cleanup_(ota::OTABackendPtr backend, const std::shared_ptr<HttpContainer> &container, bool abort_backend);
|
||||
uint8_t do_ota_();
|
||||
std::string get_url_with_auth_(const std::string &url);
|
||||
bool http_get_md5_();
|
||||
bool validate_url_(const std::string &url);
|
||||
|
||||
std::unique_ptr<Request> request_;
|
||||
std::string md5_computed_{};
|
||||
std::string md5_expected_{};
|
||||
std::string md5_url_{};
|
||||
std::string password_{};
|
||||
std::string username_{};
|
||||
std::string url_{};
|
||||
int status_ = -1;
|
||||
static const uint16_t HTTP_RECV_BUFFER = 256; // the firmware GET chunk size
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
@@ -19,23 +20,6 @@ namespace esphome::ld2450 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "ld2450");
|
||||
|
||||
enum BaudRate : uint8_t {
|
||||
BAUD_RATE_9600 = 1,
|
||||
BAUD_RATE_19200 = 2,
|
||||
BAUD_RATE_38400 = 3,
|
||||
BAUD_RATE_57600 = 4,
|
||||
BAUD_RATE_115200 = 5,
|
||||
BAUD_RATE_230400 = 6,
|
||||
BAUD_RATE_256000 = 7,
|
||||
BAUD_RATE_460800 = 8
|
||||
};
|
||||
|
||||
enum ZoneType : uint8_t {
|
||||
ZONE_DISABLED = 0,
|
||||
ZONE_DETECTION = 1,
|
||||
ZONE_FILTER = 2,
|
||||
};
|
||||
|
||||
enum PeriodicData : uint8_t {
|
||||
TARGET_X = 4,
|
||||
TARGET_Y = 6,
|
||||
@@ -54,61 +38,20 @@ enum AckData : uint8_t {
|
||||
COMMAND_STATUS = 7,
|
||||
};
|
||||
|
||||
// Memory-efficient lookup tables
|
||||
struct StringToUint8 {
|
||||
const char *str;
|
||||
const uint8_t value;
|
||||
};
|
||||
// Index + 1 is the BaudRate value; the select options use the same order
|
||||
PROGMEM_STRING_TABLE(BaudRateStrings, "9600", "19200", "38400", "57600", "115200", "230400", "256000", "460800");
|
||||
static_assert(BAUD_RATE_9600 == 1 && BAUD_RATE_460800 == BaudRateStrings::COUNT, "BaudRate is index + 1");
|
||||
// Index is the Direction value
|
||||
PROGMEM_STRING_TABLE(DirectionStrings, "Approaching", "Moving away", "Stationary", "NA");
|
||||
static_assert(DIRECTION_APPROACHING == 0 && DIRECTION_MOVING_AWAY == 1 && DIRECTION_STATIONARY == 2 &&
|
||||
DIRECTION_NA == 3,
|
||||
"Direction is the table index");
|
||||
// Index is the ZoneType value; the select options use the same order
|
||||
PROGMEM_STRING_TABLE(ZoneTypeStrings, "Disabled", "Detection", "Filter");
|
||||
static_assert(ZONE_DISABLED == 0 && ZONE_DETECTION == 1 && ZONE_FILTER == 2, "ZoneType is the table index");
|
||||
|
||||
struct Uint8ToString {
|
||||
const uint8_t value;
|
||||
const char *str;
|
||||
};
|
||||
|
||||
constexpr StringToUint8 BAUD_RATES_BY_STR[] = {
|
||||
{"9600", BAUD_RATE_9600}, {"19200", BAUD_RATE_19200}, {"38400", BAUD_RATE_38400},
|
||||
{"57600", BAUD_RATE_57600}, {"115200", BAUD_RATE_115200}, {"230400", BAUD_RATE_230400},
|
||||
{"256000", BAUD_RATE_256000}, {"460800", BAUD_RATE_460800},
|
||||
};
|
||||
|
||||
constexpr Uint8ToString DIRECTION_BY_UINT[] = {
|
||||
{DIRECTION_APPROACHING, "Approaching"},
|
||||
{DIRECTION_MOVING_AWAY, "Moving away"},
|
||||
{DIRECTION_STATIONARY, "Stationary"},
|
||||
{DIRECTION_NA, "NA"},
|
||||
};
|
||||
|
||||
constexpr Uint8ToString ZONE_TYPE_BY_UINT[] = {
|
||||
{ZONE_DISABLED, "Disabled"},
|
||||
{ZONE_DETECTION, "Detection"},
|
||||
{ZONE_FILTER, "Filter"},
|
||||
};
|
||||
|
||||
constexpr StringToUint8 ZONE_TYPE_BY_STR[] = {
|
||||
{"Disabled", ZONE_DISABLED},
|
||||
{"Detection", ZONE_DETECTION},
|
||||
{"Filter", ZONE_FILTER},
|
||||
};
|
||||
|
||||
// Baud rates in the same order as BAUD_RATES_BY_STR for index-based lookup
|
||||
constexpr uint32_t BAUD_RATES[] = {9600, 19200, 38400, 57600, 115200, 230400, 256000, 460800};
|
||||
|
||||
// Helper functions for lookups
|
||||
template<size_t N> uint8_t find_uint8(const StringToUint8 (&arr)[N], const std::string &str) {
|
||||
for (const auto &entry : arr) {
|
||||
if (str == entry.str)
|
||||
return entry.value;
|
||||
}
|
||||
return 0xFF; // Not found
|
||||
}
|
||||
|
||||
template<size_t N> const char *find_str(const Uint8ToString (&arr)[N], uint8_t value) {
|
||||
for (const auto &entry : arr) {
|
||||
if (value == entry.value)
|
||||
return entry.str;
|
||||
}
|
||||
return ""; // Not found
|
||||
}
|
||||
// Baud rates in the same order as BaudRateStrings for index-based lookup
|
||||
static constexpr uint32_t BAUD_RATES[] PROGMEM = {9600, 19200, 38400, 57600, 115200, 230400, 256000, 460800};
|
||||
|
||||
// LD2450 UART Serial Commands
|
||||
static constexpr uint8_t CMD_ENABLE_CONF = 0xFF;
|
||||
@@ -546,7 +489,7 @@ void LD2450Component::handle_periodic_data_() {
|
||||
if (this->direction_dedup_[index].next(direction)) {
|
||||
text_sensor::TextSensor *tsd = this->direction_text_sensors_[index];
|
||||
if (tsd != nullptr) {
|
||||
tsd->publish_state(find_str(ld2450::DIRECTION_BY_UINT, direction));
|
||||
tsd->publish_state(DirectionStrings::get_progmem_str(direction, DirectionStrings::LAST_INDEX));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -864,8 +807,15 @@ void LD2450Component::set_bluetooth(bool enable) {
|
||||
|
||||
// Set Baud rate
|
||||
void LD2450Component::set_baud_rate(const char *state) {
|
||||
const int16_t index = progmem_string_table_find<BaudRateStrings>(state, strlen(state));
|
||||
if (index >= 0) {
|
||||
this->set_baud_rate(static_cast<BaudRate>(index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
void LD2450Component::set_baud_rate(BaudRate rate) {
|
||||
this->set_config_mode_(true);
|
||||
const uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00};
|
||||
const uint8_t cmd_value[2] = {rate, 0x00};
|
||||
this->send_command_(CMD_SET_BAUD_RATE, cmd_value, sizeof(cmd_value));
|
||||
this->set_timeout(200, [this]() { this->restart_(); });
|
||||
}
|
||||
@@ -873,8 +823,14 @@ void LD2450Component::set_baud_rate(const char *state) {
|
||||
// Set Zone Type - one of: Disabled, Detection, Filter
|
||||
void LD2450Component::set_zone_type(const char *state) {
|
||||
ESP_LOGV(TAG, "Set zone type: %s", state);
|
||||
uint8_t zone_type = find_uint8(ZONE_TYPE_BY_STR, state);
|
||||
this->zone_type_ = zone_type;
|
||||
const int16_t index = progmem_string_table_find<ZoneTypeStrings>(state, strlen(state));
|
||||
if (index >= 0) {
|
||||
this->set_zone_type(static_cast<ZoneType>(index));
|
||||
}
|
||||
}
|
||||
|
||||
void LD2450Component::set_zone_type(ZoneType type) {
|
||||
this->zone_type_ = type;
|
||||
this->send_set_zone_command_();
|
||||
}
|
||||
|
||||
@@ -882,7 +838,7 @@ void LD2450Component::set_zone_type(const char *state) {
|
||||
void LD2450Component::publish_zone_type() {
|
||||
#ifdef USE_SELECT
|
||||
if (this->zone_type_select_ != nullptr) {
|
||||
this->zone_type_select_->publish_state(find_str(ZONE_TYPE_BY_UINT, this->zone_type_));
|
||||
this->zone_type_select_->publish_state(static_cast<size_t>(this->zone_type_));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -55,6 +55,23 @@ enum Direction : uint8_t {
|
||||
DIRECTION_UNDEFINED = 4,
|
||||
};
|
||||
|
||||
enum BaudRate : uint8_t {
|
||||
BAUD_RATE_9600 = 1,
|
||||
BAUD_RATE_19200 = 2,
|
||||
BAUD_RATE_38400 = 3,
|
||||
BAUD_RATE_57600 = 4,
|
||||
BAUD_RATE_115200 = 5,
|
||||
BAUD_RATE_230400 = 6,
|
||||
BAUD_RATE_256000 = 7,
|
||||
BAUD_RATE_460800 = 8
|
||||
};
|
||||
|
||||
enum ZoneType : uint8_t {
|
||||
ZONE_DISABLED = 0,
|
||||
ZONE_DETECTION = 1,
|
||||
ZONE_FILTER = 2,
|
||||
};
|
||||
|
||||
// Target coordinate struct
|
||||
struct Target {
|
||||
int16_t x;
|
||||
@@ -121,7 +138,9 @@ class LD2450Component : public Component, public uart::UARTDevice {
|
||||
void set_bluetooth(bool enable);
|
||||
void set_multi_target(bool enable);
|
||||
void set_baud_rate(const char *state);
|
||||
void set_baud_rate(BaudRate rate);
|
||||
void set_zone_type(const char *state);
|
||||
void set_zone_type(ZoneType type);
|
||||
void publish_zone_type();
|
||||
void factory_reset();
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
|
||||
@@ -35,6 +35,7 @@ CONFIG_SCHEMA = {
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_LD2450_ID])
|
||||
selects = select.sub_selects(config, parent=hub)
|
||||
# Option lists follow the order of the C++ enums; the hub maps by index
|
||||
await selects(
|
||||
CONF_BAUD_RATE,
|
||||
hub.set_baud_rate_select,
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace esphome::ld2450 {
|
||||
|
||||
void BaudRateSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_baud_rate(this->option_at(index));
|
||||
this->parent_->set_baud_rate(static_cast<BaudRate>(index + 1));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2450
|
||||
@@ -4,7 +4,7 @@ namespace esphome::ld2450 {
|
||||
|
||||
void ZoneTypeSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_zone_type(this->option_at(index));
|
||||
this->parent_->set_zone_type(static_cast<ZoneType>(index));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2450
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -24,10 +25,17 @@
|
||||
|
||||
namespace esphome::ld24xx {
|
||||
|
||||
// Helper to find index of value in constexpr array
|
||||
template<size_t N> optional<size_t> find_index(const uint32_t (&arr)[N], uint32_t value) {
|
||||
// Index of value in a constant table that may live in flash, or none
|
||||
template<typename T, size_t N> optional<size_t> find_index(const T (&arr)[N], T value) {
|
||||
static_assert(sizeof(T) == 1 || sizeof(T) == 4, "flash tables are read as bytes or aligned 32 bit words");
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (arr[i] == value)
|
||||
T entry;
|
||||
if constexpr (sizeof(T) == 1) {
|
||||
entry = progmem_read_byte(reinterpret_cast<const uint8_t *>(&arr[i]));
|
||||
} else {
|
||||
entry = arr[i]; // an aligned 32 bit read from flash is safe on ESP8266
|
||||
}
|
||||
if (entry == value)
|
||||
return i;
|
||||
}
|
||||
return {};
|
||||
|
||||
@@ -72,23 +72,6 @@ button:
|
||||
id(ota_update).set_source_url(std::string("http://my.ha.net:8123/local/esphome/manifest.json"));
|
||||
ESP_LOGI("test", "Manifest is now %s", id(ota_update).get_source_url());
|
||||
- component.update: ota_update
|
||||
# The OTA component's public setters and getters as lambdas use them; every shape must compile.
|
||||
- platform: template
|
||||
name: Flash from lambda
|
||||
on_press:
|
||||
then:
|
||||
- lambda: |-
|
||||
std::string url = "http://my.ha.net:8123/local/esphome/";
|
||||
url += "firmware.bin";
|
||||
id(http_request_ota).set_md5_url("http://my.ha.net:8123/local/esphome/firmware.md5");
|
||||
id(http_request_ota).set_md5(std::string("0123456789abcdef0123456789abcdef"));
|
||||
id(http_request_ota).set_username("user");
|
||||
id(http_request_ota).set_password(std::string("pass"));
|
||||
id(http_request_ota).set_url(url);
|
||||
id(http_request_ota).set_url(url.c_str());
|
||||
ESP_LOGI("test", "MD5 %s / %s", id(http_request_ota).md5_expected().c_str(),
|
||||
id(http_request_ota).md5_computed().c_str());
|
||||
id(http_request_ota).flash();
|
||||
|
||||
update:
|
||||
- platform: http_request
|
||||
|
||||
@@ -30,6 +30,7 @@ class TestableLD2450 : public LD2450Component {
|
||||
using LD2450Component::buffer_pos_;
|
||||
using LD2450Component::readline_;
|
||||
using LD2450Component::zone_config_;
|
||||
using LD2450Component::zone_type_;
|
||||
|
||||
void feed(const std::vector<uint8_t> &data) {
|
||||
for (uint8_t byte : data) {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#include <algorithm>
|
||||
#include "common.h"
|
||||
|
||||
namespace esphome::ld2450::testing {
|
||||
|
||||
namespace {
|
||||
class CapturingUARTComponent : public MockUARTComponent {
|
||||
public:
|
||||
void write_array(const uint8_t *data, size_t len) override {
|
||||
this->written.insert(this->written.end(), data, data + len);
|
||||
}
|
||||
std::vector<uint8_t> written;
|
||||
};
|
||||
|
||||
// Header, length 4, command 0xA1 (set baud rate): the rate byte and a zero follow.
|
||||
constexpr uint8_t SET_BAUD_RATE_PREFIX[] = {0xFD, 0xFC, 0xFB, 0xFA, 0x04, 0x00, 0xA1, 0x00};
|
||||
// Header, length 28, command 0xC2 (set zone): the zone type byte and a zero follow.
|
||||
constexpr uint8_t SET_ZONE_PREFIX[] = {0xFD, 0xFC, 0xFB, 0xFA, 0x1C, 0x00, 0xC2, 0x00};
|
||||
|
||||
// The two payload bytes after the first frame with this prefix, or -1 when it was not written.
|
||||
template<size_t N> std::pair<int, int> payload_after(const std::vector<uint8_t> &w, const uint8_t (&prefix)[N]) {
|
||||
auto it = std::search(w.begin(), w.end(), std::begin(prefix), std::end(prefix));
|
||||
if (it == w.end() || static_cast<size_t>(w.end() - it) < N + 2)
|
||||
return {-1, -1};
|
||||
return {*(it + N), *(it + N + 1)};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class LD2450SelectNamesTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override { this->ld2450_.set_uart_parent(&this->uart_); }
|
||||
|
||||
CapturingUARTComponent uart_;
|
||||
TestableLD2450 ld2450_;
|
||||
};
|
||||
|
||||
TEST_F(LD2450SelectNamesTest, BaudRateNamesSendTheirDeviceValue) {
|
||||
static constexpr const char *NAMES[] = {"9600", "19200", "38400", "57600", "115200", "230400", "256000", "460800"};
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
this->uart_.written.clear();
|
||||
this->ld2450_.set_baud_rate(NAMES[i]);
|
||||
const auto payload = payload_after(this->uart_.written, SET_BAUD_RATE_PREFIX);
|
||||
EXPECT_EQ(payload.first, i + 1) << NAMES[i];
|
||||
EXPECT_EQ(payload.second, 0) << NAMES[i];
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LD2450SelectNamesTest, BaudRateEnumMatchesTheSelectOrder) {
|
||||
this->ld2450_.set_baud_rate(BAUD_RATE_256000);
|
||||
EXPECT_EQ(payload_after(this->uart_.written, SET_BAUD_RATE_PREFIX).first, 7);
|
||||
}
|
||||
|
||||
TEST_F(LD2450SelectNamesTest, UnknownBaudRateNameWritesNothing) {
|
||||
this->ld2450_.set_baud_rate("1200");
|
||||
EXPECT_TRUE(this->uart_.written.empty());
|
||||
}
|
||||
|
||||
TEST_F(LD2450SelectNamesTest, ZoneTypeNamesSetTheTypeAndSendIt) {
|
||||
static constexpr const char *NAMES[] = {"Disabled", "Detection", "Filter"};
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
this->uart_.written.clear();
|
||||
this->ld2450_.set_zone_type(NAMES[i]);
|
||||
EXPECT_EQ(this->ld2450_.zone_type_, i) << NAMES[i];
|
||||
const auto payload = payload_after(this->uart_.written, SET_ZONE_PREFIX);
|
||||
EXPECT_EQ(payload.first, i) << NAMES[i];
|
||||
EXPECT_EQ(payload.second, 0) << NAMES[i];
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LD2450SelectNamesTest, ZoneTypeEnumMatchesTheSelectOrder) {
|
||||
this->ld2450_.set_zone_type(ZONE_FILTER);
|
||||
EXPECT_EQ(this->ld2450_.zone_type_, 2);
|
||||
this->ld2450_.set_zone_type(ZONE_DETECTION);
|
||||
EXPECT_EQ(this->ld2450_.zone_type_, 1);
|
||||
}
|
||||
|
||||
TEST_F(LD2450SelectNamesTest, UnknownZoneTypeNameChangesNothing) {
|
||||
this->ld2450_.set_zone_type(ZONE_DETECTION);
|
||||
this->uart_.written.clear();
|
||||
this->ld2450_.set_zone_type("Bogus");
|
||||
EXPECT_EQ(this->ld2450_.zone_type_, 1);
|
||||
EXPECT_TRUE(this->uart_.written.empty());
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2450::testing
|
||||
Reference in new issue
Block a user