Merge branch 'integration' into memory_api

This commit is contained in:
J. Nick Koston 2025-07-22 15:57:37 -10:00
commit 39f79522db
No known key found for this signature in database
11 changed files with 65 additions and 48 deletions

View File

@ -182,11 +182,14 @@ jobs:
}); });
// Check for previous comments from this workflow to avoid duplicate pings // Check for previous comments from this workflow to avoid duplicate pings
const { data: comments } = await github.rest.issues.listComments({ const comments = await github.paginate(
github.rest.issues.listComments,
{
owner, owner,
repo, repo,
issue_number: pr_number issue_number: pr_number
}); }
);
const previouslyPingedUsers = new Set(); const previouslyPingedUsers = new Set();
const previouslyPingedTeams = new Set(); const previouslyPingedTeams = new Set();
@ -194,7 +197,6 @@ jobs:
// Look for comments from github-actions bot that contain our bot marker // Look for comments from github-actions bot that contain our bot marker
const workflowComments = comments.filter(comment => const workflowComments = comments.filter(comment =>
comment.user.type === 'Bot' && comment.user.type === 'Bot' &&
comment.user.login === 'github-actions[bot]' &&
comment.body.includes(BOT_COMMENT_MARKER) comment.body.includes(BOT_COMMENT_MARKER)
); );

View File

@ -61,7 +61,8 @@ jobs:
} }
async function createComment(octokit, owner, repo, prNumber, esphomeChanges, componentChanges) { async function createComment(octokit, owner, repo, prNumber, esphomeChanges, componentChanges) {
const commentMarker = "<!-- This comment was generated automatically by a GitHub workflow. -->"; const commentMarker = "<!-- This comment was generated automatically by the external-component-bot workflow. -->";
const legacyCommentMarker = "<!-- This comment was generated automatically by a GitHub workflow. -->";
let commentBody; let commentBody;
if (esphomeChanges.length === 1) { if (esphomeChanges.length === 1) {
commentBody = generateExternalComponentInstructions(prNumber, componentChanges, owner, repo); commentBody = generateExternalComponentInstructions(prNumber, componentChanges, owner, repo);
@ -71,14 +72,23 @@ jobs:
commentBody += `\n\n---\n(Added by the PR bot)\n\n${commentMarker}`; commentBody += `\n\n---\n(Added by the PR bot)\n\n${commentMarker}`;
// Check for existing bot comment // Check for existing bot comment
const comments = await github.rest.issues.listComments({ const comments = await github.paginate(
github.rest.issues.listComments,
{
owner: owner, owner: owner,
repo: repo, repo: repo,
issue_number: prNumber, issue_number: prNumber,
}); per_page: 100,
}
);
const botComment = comments.data.find(comment => const sorted = comments.sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at));
comment.body.includes(commentMarker)
const botComment = sorted.find(comment =>
(
comment.body.includes(commentMarker) ||
comment.body.includes(legacyCommentMarker)
) && comment.user.type === "Bot"
); );
if (botComment && botComment.body === commentBody) { if (botComment && botComment.body === commentBody) {

View File

@ -29,6 +29,9 @@ jobs:
console.log(`Processing issue #${issue_number} with label: ${labelName}`); console.log(`Processing issue #${issue_number} with label: ${labelName}`);
// Hidden marker to identify bot comments from this workflow
const BOT_COMMENT_MARKER = '<!-- issue-codeowner-notify-bot -->';
// Extract component name from label // Extract component name from label
const componentName = labelName.replace('component: ', ''); const componentName = labelName.replace('component: ', '');
console.log(`Component: ${componentName}`); console.log(`Component: ${componentName}`);
@ -93,11 +96,14 @@ jobs:
); );
// Check for previous comments from this workflow to avoid duplicate pings // Check for previous comments from this workflow to avoid duplicate pings
const { data: comments } = await github.rest.issues.listComments({ const comments = await github.paginate(
github.rest.issues.listComments,
{
owner, owner,
repo, repo,
issue_number: issue_number issue_number: issue_number
}); }
);
const previouslyPingedUsers = new Set(); const previouslyPingedUsers = new Set();
const previouslyPingedTeams = new Set(); const previouslyPingedTeams = new Set();
@ -105,9 +111,8 @@ jobs:
// Look for comments from github-actions bot that contain codeowner pings for this component // Look for comments from github-actions bot that contain codeowner pings for this component
const workflowComments = comments.filter(comment => const workflowComments = comments.filter(comment =>
comment.user.type === 'Bot' && comment.user.type === 'Bot' &&
comment.user.login === 'github-actions[bot]' && comment.body.includes(BOT_COMMENT_MARKER) &&
comment.body.includes(`component: ${componentName}`) && comment.body.includes(`component: ${componentName}`)
comment.body.includes("you've been identified as a codeowner")
); );
// Extract previously mentioned users and teams from workflow comments // Extract previously mentioned users and teams from workflow comments
@ -140,7 +145,7 @@ jobs:
// Create comment body // Create comment body
const mentionString = allMentions.join(', '); const mentionString = allMentions.join(', ');
const commentBody = `👋 Hey ${mentionString}!\n\nThis issue has been labeled with \`component: ${componentName}\` and you've been identified as a codeowner of this component. Please take a look when you have a chance!\n\nThanks for maintaining this component! 🙏`; const commentBody = `${BOT_COMMENT_MARKER}\n👋 Hey ${mentionString}!\n\nThis issue has been labeled with \`component: ${componentName}\` and you've been identified as a codeowner of this component. Please take a look when you have a chance!\n\nThanks for maintaining this component! 🙏`;
// Post comment // Post comment
await github.rest.issues.createComment({ await github.rest.issues.createComment({

View File

@ -15,7 +15,7 @@ class AudioStreamInfo {
* - An audio sample represents a unit of audio for one channel. * - An audio sample represents a unit of audio for one channel.
* - A frame represents a unit of audio with a sample for every channel. * - A frame represents a unit of audio with a sample for every channel.
* *
* In gneneral, converting between bytes, samples, and frames shouldn't result in rounding errors so long as frames * In general, converting between bytes, samples, and frames shouldn't result in rounding errors so long as frames
* are used as the main unit when transferring audio data. Durations may result in rounding for certain sample rates; * are used as the main unit when transferring audio data. Durations may result in rounding for certain sample rates;
* e.g., 44.1 KHz. The ``frames_to_milliseconds_with_remainder`` function should be used for accuracy, as it takes * e.g., 44.1 KHz. The ``frames_to_milliseconds_with_remainder`` function should be used for accuracy, as it takes
* into account the remainder rather than just ignoring any rounding. * into account the remainder rather than just ignoring any rounding.
@ -76,7 +76,7 @@ class AudioStreamInfo {
/// @brief Computes the duration, in microseconds, the given amount of frames represents. /// @brief Computes the duration, in microseconds, the given amount of frames represents.
/// @param frames Number of audio frames /// @param frames Number of audio frames
/// @return Duration in microseconds `frames` respresents. May be slightly inaccurate due to integer divison rounding /// @return Duration in microseconds `frames` represents. May be slightly inaccurate due to integer division rounding
/// for certain sample rates. /// for certain sample rates.
uint32_t frames_to_microseconds(uint32_t frames) const; uint32_t frames_to_microseconds(uint32_t frames) const;

View File

@ -5,5 +5,6 @@ CODEOWNERS = ["@esphome/core"]
CONF_BYTE_ORDER = "byte_order" CONF_BYTE_ORDER = "byte_order"
CONF_COLOR_DEPTH = "color_depth" CONF_COLOR_DEPTH = "color_depth"
CONF_DRAW_ROUNDING = "draw_rounding" CONF_DRAW_ROUNDING = "draw_rounding"
CONF_ON_RECEIVE = "on_receive"
CONF_ON_STATE_CHANGE = "on_state_change" CONF_ON_STATE_CHANGE = "on_state_change"
CONF_REQUEST_HEADERS = "request_headers" CONF_REQUEST_HEADERS = "request_headers"

View File

@ -157,8 +157,8 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::perform(std::string url, std::str
container->status_code = esp_http_client_get_status_code(client); container->status_code = esp_http_client_get_status_code(client);
container->feed_wdt(); container->feed_wdt();
container->set_response_headers(user_data.response_headers); container->set_response_headers(user_data.response_headers);
if (is_success(container->status_code)) {
container->duration_ms = millis() - start; container->duration_ms = millis() - start;
if (is_success(container->status_code)) {
return container; return container;
} }
@ -191,8 +191,8 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::perform(std::string url, std::str
container->feed_wdt(); container->feed_wdt();
container->status_code = esp_http_client_get_status_code(client); container->status_code = esp_http_client_get_status_code(client);
container->feed_wdt(); container->feed_wdt();
if (is_success(container->status_code)) {
container->duration_ms = millis() - start; container->duration_ms = millis() - start;
if (is_success(container->status_code)) {
return container; return container;
} }

View File

@ -139,7 +139,7 @@ async def to_code(config):
) )
) )
cg.add_library( cg.add_library(
None, "Sensirion Gas Index Algorithm",
None, None,
"https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1", "https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1",
) )

View File

@ -1,6 +1,7 @@
from esphome import automation from esphome import automation
from esphome.automation import Trigger from esphome.automation import Trigger
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components.const import CONF_ON_RECEIVE
from esphome.components.packet_transport import ( from esphome.components.packet_transport import (
CONF_BINARY_SENSORS, CONF_BINARY_SENSORS,
CONF_ENCRYPTION, CONF_ENCRYPTION,
@ -27,7 +28,6 @@ trigger_args = cg.std_vector.template(cg.uint8)
CONF_ADDRESSES = "addresses" CONF_ADDRESSES = "addresses"
CONF_LISTEN_ADDRESS = "listen_address" CONF_LISTEN_ADDRESS = "listen_address"
CONF_UDP_ID = "udp_id" CONF_UDP_ID = "udp_id"
CONF_ON_RECEIVE = "on_receive"
CONF_LISTEN_PORT = "listen_port" CONF_LISTEN_PORT = "listen_port"
CONF_BROADCAST_PORT = "broadcast_port" CONF_BROADCAST_PORT = "broadcast_port"

View File

@ -55,6 +55,9 @@ void Application::setup() {
return a->get_actual_setup_priority() > b->get_actual_setup_priority(); return a->get_actual_setup_priority() > b->get_actual_setup_priority();
}); });
// Initialize looping_components_ early so enable_pending_loops_() works during setup
this->calculate_looping_components_();
for (uint32_t i = 0; i < this->components_.size(); i++) { for (uint32_t i = 0; i < this->components_.size(); i++) {
Component *component = this->components_[i]; Component *component = this->components_[i];
@ -97,7 +100,6 @@ void Application::setup() {
clear_setup_priority_overrides(); clear_setup_priority_overrides();
this->schedule_dump_config(); this->schedule_dump_config();
this->calculate_looping_components_();
} }
void Application::loop() { void Application::loop() {
uint8_t new_app_state = 0; uint8_t new_app_state = 0;
@ -269,24 +271,16 @@ void Application::calculate_looping_components_() {
// Pre-reserve vector to avoid reallocations // Pre-reserve vector to avoid reallocations
this->looping_components_.reserve(total_looping); this->looping_components_.reserve(total_looping);
// First add all active components // Add all components with loop override
// When called at start of setup, all components are in CONSTRUCTION state
// so none will be LOOP_DONE yet - they'll all go in the active section
for (auto *obj : this->components_) { for (auto *obj : this->components_) {
if (obj->has_overridden_loop() && if (obj->has_overridden_loop()) {
(obj->get_component_state() & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) {
this->looping_components_.push_back(obj); this->looping_components_.push_back(obj);
} }
} }
this->looping_components_active_end_ = this->looping_components_.size(); this->looping_components_active_end_ = this->looping_components_.size();
// Then add all inactive (LOOP_DONE) components
// This handles components that called disable_loop() during setup, before this method runs
for (auto *obj : this->components_) {
if (obj->has_overridden_loop() &&
(obj->get_component_state() & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) {
this->looping_components_.push_back(obj);
}
}
} }
void Application::disable_component_loop_(Component *component) { void Application::disable_component_loop_(Component *component) {

View File

@ -29,7 +29,7 @@ namespace esphome {
// Base lock-free queue without task notification // Base lock-free queue without task notification
template<class T, uint8_t SIZE> class LockFreeQueue { template<class T, uint8_t SIZE> class LockFreeQueue {
public: public:
LockFreeQueue() : head_(0), tail_(0), dropped_count_(0) {} LockFreeQueue() : dropped_count_(0), head_(0), tail_(0) {}
bool push(T *element) { bool push(T *element) {
bool was_empty; bool was_empty;

View File

@ -411,11 +411,16 @@ def add_referenced_recursive(referenced_schemas, config_var, path, eat_schema=Fa
s1 = get_str_path_schema(k) s1 = get_str_path_schema(k)
p = k.split(".") p = k.split(".")
if len(p) == 3 and path[0] == f"{p[0]}.{p[1]}": if len(p) == 3:
if path[0] == f"{p[0]}.{p[1]}":
# special case for schema inside platforms # special case for schema inside platforms
add_referenced_recursive( add_referenced_recursive(
referenced_schemas, s1, [path[0], "schemas", p[2]] referenced_schemas, s1, [path[0], "schemas", p[2]]
) )
else:
add_referenced_recursive(
referenced_schemas, s1, [f"{p[0]}.{p[1]}", "schemas", p[2]]
)
else: else:
add_referenced_recursive( add_referenced_recursive(
referenced_schemas, s1, [p[0], "schemas", p[1]] referenced_schemas, s1, [p[0], "schemas", p[1]]