mirror of
https://github.com/esphome/esphome.git
synced 2025-02-21 02:32:26 +01:00
Merge branch 'dev' into fix_rc5_recv
This commit is contained in:
commit
1646bcced8
@ -11,14 +11,6 @@ repos:
|
||||
args: [--fix]
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 24.4.2
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
- --safe
|
||||
- --quiet
|
||||
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.1.0
|
||||
hooks:
|
||||
@ -53,6 +45,6 @@ repos:
|
||||
hooks:
|
||||
- id: pylint
|
||||
name: pylint
|
||||
entry: script/run-in-env.sh pylint
|
||||
language: script
|
||||
entry: python script/run-in-env pylint
|
||||
language: system
|
||||
types: [python]
|
||||
|
@ -298,6 +298,12 @@ void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
|
||||
if (this->cell_16_voltage_sensor_) {
|
||||
this->cell_16_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
|
||||
}
|
||||
if (this->cell_17_voltage_sensor_) {
|
||||
this->cell_17_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
|
||||
}
|
||||
if (this->cell_18_voltage_sensor_) {
|
||||
this->cell_18_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -54,6 +54,8 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
|
||||
SUB_SENSOR(cell_14_voltage)
|
||||
SUB_SENSOR(cell_15_voltage)
|
||||
SUB_SENSOR(cell_16_voltage)
|
||||
SUB_SENSOR(cell_17_voltage)
|
||||
SUB_SENSOR(cell_18_voltage)
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
|
@ -52,6 +52,8 @@ CONF_CELL_13_VOLTAGE = "cell_13_voltage"
|
||||
CONF_CELL_14_VOLTAGE = "cell_14_voltage"
|
||||
CONF_CELL_15_VOLTAGE = "cell_15_voltage"
|
||||
CONF_CELL_16_VOLTAGE = "cell_16_voltage"
|
||||
CONF_CELL_17_VOLTAGE = "cell_17_voltage"
|
||||
CONF_CELL_18_VOLTAGE = "cell_18_voltage"
|
||||
ICON_CURRENT_DC = "mdi:current-dc"
|
||||
ICON_BATTERY_OUTLINE = "mdi:battery-outline"
|
||||
ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up"
|
||||
@ -92,6 +94,8 @@ TYPES = [
|
||||
CONF_CELL_14_VOLTAGE,
|
||||
CONF_CELL_15_VOLTAGE,
|
||||
CONF_CELL_16_VOLTAGE,
|
||||
CONF_CELL_17_VOLTAGE,
|
||||
CONF_CELL_18_VOLTAGE,
|
||||
]
|
||||
|
||||
CELL_VOLTAGE_SCHEMA = sensor.sensor_schema(
|
||||
@ -212,6 +216,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_CELL_14_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_15_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_16_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_17_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_18_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "esp_crt_bundle.h"
|
||||
#endif
|
||||
|
||||
#include "esp_task_wdt.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
|
||||
@ -117,11 +119,11 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::strin
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
container->content_length = esp_http_client_fetch_headers(client);
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
container->status_code = esp_http_client_get_status_code(client);
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
if (is_success(container->status_code)) {
|
||||
container->duration_ms = millis() - start;
|
||||
return container;
|
||||
@ -151,11 +153,11 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::strin
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
container->content_length = esp_http_client_fetch_headers(client);
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
container->status_code = esp_http_client_get_status_code(client);
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
if (is_success(container->status_code)) {
|
||||
container->duration_ms = millis() - start;
|
||||
return container;
|
||||
@ -185,8 +187,9 @@ int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
App.feed_wdt();
|
||||
this->feed_wdt();
|
||||
int read_len = esp_http_client_read(this->client_, (char *) buf, bufsize);
|
||||
this->feed_wdt();
|
||||
this->bytes_read_ += read_len;
|
||||
|
||||
this->duration_ms += (millis() - start);
|
||||
@ -201,6 +204,13 @@ void HttpContainerIDF::end() {
|
||||
esp_http_client_cleanup(this->client_);
|
||||
}
|
||||
|
||||
void HttpContainerIDF::feed_wdt() {
|
||||
// Tests to see if the executing task has a watchdog timer attached
|
||||
if (esp_task_wdt_status(nullptr) == ESP_OK) {
|
||||
App.feed_wdt();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
||||
|
||||
|
@ -18,6 +18,9 @@ class HttpContainerIDF : public HttpContainer {
|
||||
int read(uint8_t *buf, size_t max_len) override;
|
||||
void end() override;
|
||||
|
||||
/// @brief Feeds the watchdog timer if the executing task has one attached
|
||||
void feed_wdt();
|
||||
|
||||
protected:
|
||||
esp_http_client_handle_t client_;
|
||||
};
|
||||
|
@ -9,6 +9,13 @@
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
|
||||
// The update function runs in a task only on ESP32s.
|
||||
#ifdef USE_ESP32
|
||||
#define UPDATE_RETURN vTaskDelete(nullptr) // Delete the current update task
|
||||
#else
|
||||
#define UPDATE_RETURN return
|
||||
#endif
|
||||
|
||||
static const char *const TAG = "http_request.update";
|
||||
|
||||
static const size_t MAX_READ_SIZE = 256;
|
||||
@ -29,113 +36,131 @@ void HttpRequestUpdate::setup() {
|
||||
}
|
||||
|
||||
void HttpRequestUpdate::update() {
|
||||
auto container = this->request_parent_->get(this->source_url_);
|
||||
#ifdef USE_ESP32
|
||||
xTaskCreate(HttpRequestUpdate::update_task, "update_task", 8192, (void *) this, 1, &this->update_task_handle_);
|
||||
#else
|
||||
this->update_task(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void HttpRequestUpdate::update_task(void *params) {
|
||||
HttpRequestUpdate *this_update = (HttpRequestUpdate *) params;
|
||||
|
||||
auto container = this_update->request_parent_->get(this_update->source_url_);
|
||||
|
||||
if (container == nullptr || container->status_code != HTTP_STATUS_OK) {
|
||||
std::string msg = str_sprintf("Failed to fetch manifest from %s", this->source_url_.c_str());
|
||||
this->status_set_error(msg.c_str());
|
||||
return;
|
||||
std::string msg = str_sprintf("Failed to fetch manifest from %s", this_update->source_url_.c_str());
|
||||
this_update->status_set_error(msg.c_str());
|
||||
UPDATE_RETURN;
|
||||
}
|
||||
|
||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||
uint8_t *data = allocator.allocate(container->content_length);
|
||||
if (data == nullptr) {
|
||||
std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length);
|
||||
this->status_set_error(msg.c_str());
|
||||
this_update->status_set_error(msg.c_str());
|
||||
container->end();
|
||||
return;
|
||||
UPDATE_RETURN;
|
||||
}
|
||||
|
||||
size_t read_index = 0;
|
||||
while (container->get_bytes_read() < container->content_length) {
|
||||
int read_bytes = container->read(data + read_index, MAX_READ_SIZE);
|
||||
|
||||
App.feed_wdt();
|
||||
yield();
|
||||
|
||||
read_index += read_bytes;
|
||||
}
|
||||
|
||||
std::string response((char *) data, read_index);
|
||||
allocator.deallocate(data, container->content_length);
|
||||
bool valid = false;
|
||||
{ // Ensures the response string falls out of scope and deallocates before the task ends
|
||||
std::string response((char *) data, read_index);
|
||||
allocator.deallocate(data, container->content_length);
|
||||
|
||||
container->end();
|
||||
container->end();
|
||||
container.reset(); // Release ownership of the container's shared_ptr
|
||||
|
||||
bool valid = json::parse_json(response, [this](JsonObject root) -> bool {
|
||||
if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
}
|
||||
this->update_info_.title = root["name"].as<std::string>();
|
||||
this->update_info_.latest_version = root["version"].as<std::string>();
|
||||
|
||||
for (auto build : root["builds"].as<JsonArray>()) {
|
||||
if (!build.containsKey("chipFamily")) {
|
||||
valid = json::parse_json(response, [this_update](JsonObject root) -> bool {
|
||||
if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
}
|
||||
if (build["chipFamily"] == ESPHOME_VARIANT) {
|
||||
if (!build.containsKey("ota")) {
|
||||
this_update->update_info_.title = root["name"].as<std::string>();
|
||||
this_update->update_info_.latest_version = root["version"].as<std::string>();
|
||||
|
||||
for (auto build : root["builds"].as<JsonArray>()) {
|
||||
if (!build.containsKey("chipFamily")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
}
|
||||
auto ota = build["ota"];
|
||||
if (!ota.containsKey("path") || !ota.containsKey("md5")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
if (build["chipFamily"] == ESPHOME_VARIANT) {
|
||||
if (!build.containsKey("ota")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
}
|
||||
auto ota = build["ota"];
|
||||
if (!ota.containsKey("path") || !ota.containsKey("md5")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
}
|
||||
this_update->update_info_.firmware_url = ota["path"].as<std::string>();
|
||||
this_update->update_info_.md5 = ota["md5"].as<std::string>();
|
||||
|
||||
if (ota.containsKey("summary"))
|
||||
this_update->update_info_.summary = ota["summary"].as<std::string>();
|
||||
if (ota.containsKey("release_url"))
|
||||
this_update->update_info_.release_url = ota["release_url"].as<std::string>();
|
||||
|
||||
return true;
|
||||
}
|
||||
this->update_info_.firmware_url = ota["path"].as<std::string>();
|
||||
this->update_info_.md5 = ota["md5"].as<std::string>();
|
||||
|
||||
if (ota.containsKey("summary"))
|
||||
this->update_info_.summary = ota["summary"].as<std::string>();
|
||||
if (ota.containsKey("release_url"))
|
||||
this->update_info_.release_url = ota["release_url"].as<std::string>();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
std::string msg = str_sprintf("Failed to parse JSON from %s", this->source_url_.c_str());
|
||||
this->status_set_error(msg.c_str());
|
||||
return;
|
||||
std::string msg = str_sprintf("Failed to parse JSON from %s", this_update->source_url_.c_str());
|
||||
this_update->status_set_error(msg.c_str());
|
||||
UPDATE_RETURN;
|
||||
}
|
||||
|
||||
// Merge source_url_ and this->update_info_.firmware_url
|
||||
if (this->update_info_.firmware_url.find("http") == std::string::npos) {
|
||||
std::string path = this->update_info_.firmware_url;
|
||||
// Merge source_url_ and this_update->update_info_.firmware_url
|
||||
if (this_update->update_info_.firmware_url.find("http") == std::string::npos) {
|
||||
std::string path = this_update->update_info_.firmware_url;
|
||||
if (path[0] == '/') {
|
||||
std::string domain = this->source_url_.substr(0, this->source_url_.find('/', 8));
|
||||
this->update_info_.firmware_url = domain + path;
|
||||
std::string domain = this_update->source_url_.substr(0, this_update->source_url_.find('/', 8));
|
||||
this_update->update_info_.firmware_url = domain + path;
|
||||
} else {
|
||||
std::string domain = this->source_url_.substr(0, this->source_url_.rfind('/') + 1);
|
||||
this->update_info_.firmware_url = domain + path;
|
||||
std::string domain = this_update->source_url_.substr(0, this_update->source_url_.rfind('/') + 1);
|
||||
this_update->update_info_.firmware_url = domain + path;
|
||||
}
|
||||
}
|
||||
|
||||
std::string current_version;
|
||||
{ // Ensures the current version string falls out of scope and deallocates before the task ends
|
||||
std::string current_version;
|
||||
#ifdef ESPHOME_PROJECT_VERSION
|
||||
current_version = ESPHOME_PROJECT_VERSION;
|
||||
current_version = ESPHOME_PROJECT_VERSION;
|
||||
#else
|
||||
current_version = ESPHOME_VERSION;
|
||||
current_version = ESPHOME_VERSION;
|
||||
#endif
|
||||
|
||||
this->update_info_.current_version = current_version;
|
||||
|
||||
if (this->update_info_.latest_version.empty() || this->update_info_.latest_version == update_info_.current_version) {
|
||||
this->state_ = update::UPDATE_STATE_NO_UPDATE;
|
||||
} else {
|
||||
this->state_ = update::UPDATE_STATE_AVAILABLE;
|
||||
this_update->update_info_.current_version = current_version;
|
||||
}
|
||||
|
||||
this->update_info_.has_progress = false;
|
||||
this->update_info_.progress = 0.0f;
|
||||
if (this_update->update_info_.latest_version.empty() ||
|
||||
this_update->update_info_.latest_version == this_update->update_info_.current_version) {
|
||||
this_update->state_ = update::UPDATE_STATE_NO_UPDATE;
|
||||
} else {
|
||||
this_update->state_ = update::UPDATE_STATE_AVAILABLE;
|
||||
}
|
||||
|
||||
this->status_clear_error();
|
||||
this->publish_state();
|
||||
this_update->update_info_.has_progress = false;
|
||||
this_update->update_info_.progress = 0.0f;
|
||||
|
||||
this_update->status_clear_error();
|
||||
this_update->publish_state();
|
||||
|
||||
UPDATE_RETURN;
|
||||
}
|
||||
|
||||
void HttpRequestUpdate::perform(bool force) {
|
||||
|
@ -7,6 +7,10 @@
|
||||
#include "esphome/components/http_request/ota/ota_http_request.h"
|
||||
#include "esphome/components/update/update_entity.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
|
||||
@ -29,6 +33,11 @@ class HttpRequestUpdate : public update::UpdateEntity, public PollingComponent {
|
||||
HttpRequestComponent *request_parent_;
|
||||
OtaHttpRequestComponent *ota_parent_;
|
||||
std::string source_url_;
|
||||
|
||||
static void update_task(void *params);
|
||||
#ifdef USE_ESP32
|
||||
TaskHandle_t update_task_handle_{nullptr};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace http_request
|
||||
|
53
script/run-in-env
Normal file
53
script/run-in-env
Normal file
@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def find_and_activate_virtualenv():
|
||||
try:
|
||||
# Get the top-level directory of the git repository
|
||||
my_path = subprocess.check_output(
|
||||
["git", "rev-parse", "--show-toplevel"], text=True
|
||||
).strip()
|
||||
except subprocess.CalledProcessError:
|
||||
print(
|
||||
"Error: Not a git repository or unable to determine the top-level directory.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Check for virtual environments
|
||||
for venv in ["venv", ".venv", "."]:
|
||||
activate_path = (
|
||||
Path(my_path)
|
||||
/ venv
|
||||
/ ("Scripts" if os.name == "nt" else "bin")
|
||||
/ "activate"
|
||||
)
|
||||
if activate_path.exists():
|
||||
# Activate the virtual environment by updating PATH
|
||||
env = os.environ.copy()
|
||||
venv_bin_dir = activate_path.parent
|
||||
env["PATH"] = f"{venv_bin_dir}{os.pathsep}{env['PATH']}"
|
||||
env["VIRTUAL_ENV"] = str(venv_bin_dir.parent)
|
||||
print(f"Activated virtual environment: {venv_bin_dir.parent}")
|
||||
|
||||
# Execute the remaining arguments in the new environment
|
||||
if len(sys.argv) > 1:
|
||||
subprocess.run(sys.argv[1:], env=env, check=False)
|
||||
else:
|
||||
print(
|
||||
"No command provided to run in the virtual environment.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return
|
||||
|
||||
print("No virtual environment found.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
find_and_activate_virtualenv()
|
@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
my_path=$(git rev-parse --show-toplevel)
|
||||
|
||||
for venv in venv .venv .; do
|
||||
if [ -f "${my_path}/${venv}/bin/activate" ]; then
|
||||
. "${my_path}/${venv}/bin/activate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
exec "$@"
|
Loading…
Reference in New Issue
Block a user