mirror of
https://github.com/esphome/esphome.git
synced 2025-09-01 19:02:18 +01:00
Merge branch 'integration' into memory_api
This commit is contained in:
@@ -9,7 +9,7 @@ This document provides essential context for AI models interacting with this pro
|
|||||||
|
|
||||||
## 2. Core Technologies & Stack
|
## 2. Core Technologies & Stack
|
||||||
|
|
||||||
* **Languages:** Python (>=3.10), C++ (gnu++20)
|
* **Languages:** Python (>=3.11), C++ (gnu++20)
|
||||||
* **Frameworks & Runtimes:** PlatformIO, Arduino, ESP-IDF.
|
* **Frameworks & Runtimes:** PlatformIO, Arduino, ESP-IDF.
|
||||||
* **Build Systems:** PlatformIO is the primary build system. CMake is used as an alternative.
|
* **Build Systems:** PlatformIO is the primary build system. CMake is used as an alternative.
|
||||||
* **Configuration:** YAML.
|
* **Configuration:** YAML.
|
||||||
@@ -38,7 +38,7 @@ This document provides essential context for AI models interacting with this pro
|
|||||||
5. **Dashboard** (`esphome/dashboard/`): A web-based interface for device configuration, management, and OTA updates.
|
5. **Dashboard** (`esphome/dashboard/`): A web-based interface for device configuration, management, and OTA updates.
|
||||||
|
|
||||||
* **Platform Support:**
|
* **Platform Support:**
|
||||||
1. **ESP32** (`components/esp32/`): Espressif ESP32 family. Supports multiple variants (S2, S3, C3, etc.) and both IDF and Arduino frameworks.
|
1. **ESP32** (`components/esp32/`): Espressif ESP32 family. Supports multiple variants (Original, C2, C3, C5, C6, H2, P4, S2, S3) with ESP-IDF framework. Arduino framework supports only a subset of the variants (Original, C3, S2, S3).
|
||||||
2. **ESP8266** (`components/esp8266/`): Espressif ESP8266. Arduino framework only, with memory constraints.
|
2. **ESP8266** (`components/esp8266/`): Espressif ESP8266. Arduino framework only, with memory constraints.
|
||||||
3. **RP2040** (`components/rp2040/`): Raspberry Pi Pico/RP2040. Arduino framework with PIO (Programmable I/O) support.
|
3. **RP2040** (`components/rp2040/`): Raspberry Pi Pico/RP2040. Arduino framework with PIO (Programmable I/O) support.
|
||||||
4. **LibreTiny** (`components/libretiny/`): Realtek and Beken chips. Supports multiple chip families and auto-generated components.
|
4. **LibreTiny** (`components/libretiny/`): Realtek and Beken chips. Supports multiple chip families and auto-generated components.
|
||||||
@@ -60,7 +60,7 @@ This document provides essential context for AI models interacting with this pro
|
|||||||
├── __init__.py # Component configuration schema and code generation
|
├── __init__.py # Component configuration schema and code generation
|
||||||
├── [component].h # C++ header file (if needed)
|
├── [component].h # C++ header file (if needed)
|
||||||
├── [component].cpp # C++ implementation (if needed)
|
├── [component].cpp # C++ implementation (if needed)
|
||||||
└── [platform]/ # Platform-specific implementations
|
└── [platform]/ # Platform-specific implementations
|
||||||
├── __init__.py # Platform-specific configuration
|
├── __init__.py # Platform-specific configuration
|
||||||
├── [platform].h # Platform C++ header
|
├── [platform].h # Platform C++ header
|
||||||
└── [platform].cpp # Platform C++ implementation
|
└── [platform].cpp # Platform C++ implementation
|
||||||
@@ -150,7 +150,8 @@ This document provides essential context for AI models interacting with this pro
|
|||||||
* **Configuration Validation:**
|
* **Configuration Validation:**
|
||||||
* **Common Validators:** `cv.int_`, `cv.float_`, `cv.string`, `cv.boolean`, `cv.int_range(min=0, max=100)`, `cv.positive_int`, `cv.percentage`.
|
* **Common Validators:** `cv.int_`, `cv.float_`, `cv.string`, `cv.boolean`, `cv.int_range(min=0, max=100)`, `cv.positive_int`, `cv.percentage`.
|
||||||
* **Complex Validation:** `cv.All(cv.string, cv.Length(min=1, max=50))`, `cv.Any(cv.int_, cv.string)`.
|
* **Complex Validation:** `cv.All(cv.string, cv.Length(min=1, max=50))`, `cv.Any(cv.int_, cv.string)`.
|
||||||
* **Platform-Specific:** `cv.only_on(["esp32", "esp8266"])`, `cv.only_with_arduino`.
|
* **Platform-Specific:** `cv.only_on(["esp32", "esp8266"])`, `esp32.only_on_variant(...)`, `cv.only_on_esp32`, `cv.only_on_esp8266`, `cv.only_on_rp2040`.
|
||||||
|
* **Framework-Specific:** `cv.only_with_framework(...)`, `cv.only_with_arduino`, `cv.only_with_esp_idf`.
|
||||||
* **Schema Extensions:**
|
* **Schema Extensions:**
|
||||||
```python
|
```python
|
||||||
CONFIG_SCHEMA = cv.Schema({ ... })
|
CONFIG_SCHEMA = cv.Schema({ ... })
|
||||||
|
@@ -1 +1 @@
|
|||||||
0440e35cf89a49e8a35fd3690ed453a72b7b6f61b9d346ced6140e1c0d39dff6
|
4368db58e8f884aff245996b1e8b644cc0796c0bb2fa706d5740d40b823d3ac9
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "deep_sleep_component.h"
|
#include "deep_sleep_component.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
@@ -83,7 +84,11 @@ void DeepSleepComponent::deep_sleep_() {
|
|||||||
}
|
}
|
||||||
gpio_sleep_set_direction(gpio_pin, GPIO_MODE_INPUT);
|
gpio_sleep_set_direction(gpio_pin, GPIO_MODE_INPUT);
|
||||||
gpio_hold_en(gpio_pin);
|
gpio_hold_en(gpio_pin);
|
||||||
|
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
|
||||||
|
// Some ESP32 variants support holding a single GPIO during deep sleep without this function
|
||||||
|
// For those variants, gpio_hold_en() is sufficient to hold the pin state during deep sleep
|
||||||
gpio_deep_sleep_hold_en();
|
gpio_deep_sleep_hold_en();
|
||||||
|
#endif
|
||||||
bool level = !this->wakeup_pin_->is_inverted();
|
bool level = !this->wakeup_pin_->is_inverted();
|
||||||
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
|
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
|
||||||
level = !level;
|
level = !level;
|
||||||
@@ -120,7 +125,11 @@ void DeepSleepComponent::deep_sleep_() {
|
|||||||
}
|
}
|
||||||
gpio_sleep_set_direction(gpio_pin, GPIO_MODE_INPUT);
|
gpio_sleep_set_direction(gpio_pin, GPIO_MODE_INPUT);
|
||||||
gpio_hold_en(gpio_pin);
|
gpio_hold_en(gpio_pin);
|
||||||
|
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
|
||||||
|
// Some ESP32 variants support holding a single GPIO during deep sleep without this function
|
||||||
|
// For those variants, gpio_hold_en() is sufficient to hold the pin state during deep sleep
|
||||||
gpio_deep_sleep_hold_en();
|
gpio_deep_sleep_hold_en();
|
||||||
|
#endif
|
||||||
bool level = !this->wakeup_pin_->is_inverted();
|
bool level = !this->wakeup_pin_->is_inverted();
|
||||||
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
|
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
|
||||||
level = !level;
|
level = !level;
|
||||||
|
@@ -5,9 +5,14 @@ from esphome import automation
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
|
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ESPHOME, CONF_ID, CONF_NAME
|
from esphome.const import (
|
||||||
|
CONF_ENABLE_ON_BOOT,
|
||||||
|
CONF_ESPHOME,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_NAME_ADD_MAC_SUFFIX,
|
||||||
|
)
|
||||||
from esphome.core import CORE, TimePeriod
|
from esphome.core import CORE, TimePeriod
|
||||||
from esphome.core.config import CONF_NAME_ADD_MAC_SUFFIX
|
|
||||||
import esphome.final_validate as fv
|
import esphome.final_validate as fv
|
||||||
|
|
||||||
DEPENDENCIES = ["esp32"]
|
DEPENDENCIES = ["esp32"]
|
||||||
|
@@ -298,7 +298,7 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
|||||||
// This should not happen but lets log it in case it does
|
// This should not happen but lets log it in case it does
|
||||||
// because it means we have a bad assumption about how the
|
// because it means we have a bad assumption about how the
|
||||||
// ESP BT stack works.
|
// 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] ESP_GATTC_OPEN_EVT in %s state (status=%d)", this->connection_index_,
|
||||||
this->address_str_.c_str(), espbt::client_state_to_string(this->state_), param->open.status);
|
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) {
|
if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) {
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
#include "http_request_host.h"
|
|
||||||
|
|
||||||
#ifdef USE_HOST
|
#ifdef USE_HOST
|
||||||
|
|
||||||
|
#define USE_HTTP_REQUEST_HOST_H
|
||||||
|
#define CPPHTTPLIB_NO_EXCEPTIONS
|
||||||
|
#include "httplib.h"
|
||||||
|
#include "http_request_host.h"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include "esphome/components/network/util.h"
|
#include "esphome/components/network/util.h"
|
||||||
#include "esphome/components/watchdog/watchdog.h"
|
#include "esphome/components/watchdog/watchdog.h"
|
||||||
|
@@ -1,11 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "http_request.h"
|
|
||||||
|
|
||||||
#ifdef USE_HOST
|
#ifdef USE_HOST
|
||||||
|
#include "http_request.h"
|
||||||
#define CPPHTTPLIB_NO_EXCEPTIONS
|
|
||||||
#include "httplib.h"
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace http_request {
|
namespace http_request {
|
||||||
|
|
||||||
|
@@ -3,12 +3,10 @@
|
|||||||
/**
|
/**
|
||||||
* NOTE: This is a copy of httplib.h from https://github.com/yhirose/cpp-httplib
|
* NOTE: This is a copy of httplib.h from https://github.com/yhirose/cpp-httplib
|
||||||
*
|
*
|
||||||
* It has been modified only to add ifdefs for USE_HOST. While it contains many functions unused in ESPHome,
|
* It has been modified to add ifdefs for USE_HOST. While it contains many functions unused in ESPHome,
|
||||||
* it was considered preferable to use it with as few changes as possible, to facilitate future updates.
|
* it was considered preferable to use it with as few changes as possible, to facilitate future updates.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// httplib.h
|
// httplib.h
|
||||||
//
|
//
|
||||||
@@ -17,6 +15,11 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#ifdef USE_HOST
|
#ifdef USE_HOST
|
||||||
|
// Prevent this code being included in main.cpp
|
||||||
|
#ifdef USE_HTTP_REQUEST_HOST_H
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
#ifndef CPPHTTPLIB_HTTPLIB_H
|
#ifndef CPPHTTPLIB_HTTPLIB_H
|
||||||
#define CPPHTTPLIB_HTTPLIB_H
|
#define CPPHTTPLIB_HTTPLIB_H
|
||||||
|
|
||||||
@@ -9687,5 +9690,6 @@ inline SSL_CTX *Client::ssl_context() const {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // CPPHTTPLIB_HTTPLIB_H
|
#endif // CPPHTTPLIB_HTTPLIB_H
|
||||||
|
#endif // USE_HTTP_REQUEST_HOST_H
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -24,7 +24,7 @@ from ..defines import (
|
|||||||
literal,
|
literal,
|
||||||
)
|
)
|
||||||
from ..lv_validation import (
|
from ..lv_validation import (
|
||||||
lv_angle,
|
lv_angle_degrees,
|
||||||
lv_bool,
|
lv_bool,
|
||||||
lv_color,
|
lv_color,
|
||||||
lv_image,
|
lv_image,
|
||||||
@@ -395,15 +395,15 @@ ARC_PROPS = {
|
|||||||
DRAW_OPA_SCHEMA.extend(
|
DRAW_OPA_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_RADIUS): pixels,
|
cv.Required(CONF_RADIUS): pixels,
|
||||||
cv.Required(CONF_START_ANGLE): lv_angle,
|
cv.Required(CONF_START_ANGLE): lv_angle_degrees,
|
||||||
cv.Required(CONF_END_ANGLE): lv_angle,
|
cv.Required(CONF_END_ANGLE): lv_angle_degrees,
|
||||||
}
|
}
|
||||||
).extend({cv.Optional(prop): validator for prop, validator in ARC_PROPS.items()}),
|
).extend({cv.Optional(prop): validator for prop, validator in ARC_PROPS.items()}),
|
||||||
)
|
)
|
||||||
async def canvas_draw_arc(config, action_id, template_arg, args):
|
async def canvas_draw_arc(config, action_id, template_arg, args):
|
||||||
radius = await size.process(config[CONF_RADIUS])
|
radius = await size.process(config[CONF_RADIUS])
|
||||||
start_angle = await lv_angle.process(config[CONF_START_ANGLE])
|
start_angle = await lv_angle_degrees.process(config[CONF_START_ANGLE])
|
||||||
end_angle = await lv_angle.process(config[CONF_END_ANGLE])
|
end_angle = await lv_angle_degrees.process(config[CONF_END_ANGLE])
|
||||||
|
|
||||||
async def do_draw_arc(w: Widget, x, y, dsc_addr):
|
async def do_draw_arc(w: Widget, x, y, dsc_addr):
|
||||||
lv.canvas_draw_arc(w.obj, x, y, radius, start_angle, end_angle, dsc_addr)
|
lv.canvas_draw_arc(w.obj, x, y, radius, start_angle, end_angle, dsc_addr)
|
||||||
|
@@ -14,7 +14,6 @@ from esphome.const import (
|
|||||||
CONF_VALUE,
|
CONF_VALUE,
|
||||||
CONF_WIDTH,
|
CONF_WIDTH,
|
||||||
)
|
)
|
||||||
from esphome.cpp_generator import IntLiteral
|
|
||||||
|
|
||||||
from ..automation import action_to_code
|
from ..automation import action_to_code
|
||||||
from ..defines import (
|
from ..defines import (
|
||||||
@@ -32,7 +31,7 @@ from ..helpers import add_lv_use, lvgl_components_required
|
|||||||
from ..lv_validation import (
|
from ..lv_validation import (
|
||||||
get_end_value,
|
get_end_value,
|
||||||
get_start_value,
|
get_start_value,
|
||||||
lv_angle,
|
lv_angle_degrees,
|
||||||
lv_bool,
|
lv_bool,
|
||||||
lv_color,
|
lv_color,
|
||||||
lv_float,
|
lv_float,
|
||||||
@@ -163,7 +162,7 @@ SCALE_SCHEMA = cv.Schema(
|
|||||||
cv.Optional(CONF_RANGE_FROM, default=0.0): cv.float_,
|
cv.Optional(CONF_RANGE_FROM, default=0.0): cv.float_,
|
||||||
cv.Optional(CONF_RANGE_TO, default=100.0): cv.float_,
|
cv.Optional(CONF_RANGE_TO, default=100.0): cv.float_,
|
||||||
cv.Optional(CONF_ANGLE_RANGE, default=270): cv.int_range(0, 360),
|
cv.Optional(CONF_ANGLE_RANGE, default=270): cv.int_range(0, 360),
|
||||||
cv.Optional(CONF_ROTATION): lv_angle,
|
cv.Optional(CONF_ROTATION): lv_angle_degrees,
|
||||||
cv.Optional(CONF_INDICATORS): cv.ensure_list(INDICATOR_SCHEMA),
|
cv.Optional(CONF_INDICATORS): cv.ensure_list(INDICATOR_SCHEMA),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -188,9 +187,7 @@ class MeterType(WidgetType):
|
|||||||
for scale_conf in config.get(CONF_SCALES, ()):
|
for scale_conf in config.get(CONF_SCALES, ()):
|
||||||
rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2
|
rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2
|
||||||
if CONF_ROTATION in scale_conf:
|
if CONF_ROTATION in scale_conf:
|
||||||
rotation = await lv_angle.process(scale_conf[CONF_ROTATION])
|
rotation = await lv_angle_degrees.process(scale_conf[CONF_ROTATION])
|
||||||
if isinstance(rotation, IntLiteral):
|
|
||||||
rotation = int(str(rotation)) // 10
|
|
||||||
with LocalVariable(
|
with LocalVariable(
|
||||||
"meter_var", "lv_meter_scale_t", lv_expr.meter_add_scale(var)
|
"meter_var", "lv_meter_scale_t", lv_expr.meter_add_scale(var)
|
||||||
) as meter_var:
|
) as meter_var:
|
||||||
|
@@ -255,4 +255,233 @@ DriverChip(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DriverChip(
|
||||||
|
"JC3636W518V2",
|
||||||
|
height=360,
|
||||||
|
width=360,
|
||||||
|
offset_height=1,
|
||||||
|
draw_rounding=1,
|
||||||
|
cs_pin=10,
|
||||||
|
reset_pin=47,
|
||||||
|
invert_colors=True,
|
||||||
|
color_order=MODE_RGB,
|
||||||
|
bus_mode=TYPE_QUAD,
|
||||||
|
data_rate="40MHz",
|
||||||
|
initsequence=(
|
||||||
|
(0xF0, 0x28),
|
||||||
|
(0xF2, 0x28),
|
||||||
|
(0x73, 0xF0),
|
||||||
|
(0x7C, 0xD1),
|
||||||
|
(0x83, 0xE0),
|
||||||
|
(0x84, 0x61),
|
||||||
|
(0xF2, 0x82),
|
||||||
|
(0xF0, 0x00),
|
||||||
|
(0xF0, 0x01),
|
||||||
|
(0xF1, 0x01),
|
||||||
|
(0xB0, 0x56),
|
||||||
|
(0xB1, 0x4D),
|
||||||
|
(0xB2, 0x24),
|
||||||
|
(0xB4, 0x87),
|
||||||
|
(0xB5, 0x44),
|
||||||
|
(0xB6, 0x8B),
|
||||||
|
(0xB7, 0x40),
|
||||||
|
(0xB8, 0x86),
|
||||||
|
(0xBA, 0x00),
|
||||||
|
(0xBB, 0x08),
|
||||||
|
(0xBC, 0x08),
|
||||||
|
(0xBD, 0x00),
|
||||||
|
(0xC0, 0x80),
|
||||||
|
(0xC1, 0x10),
|
||||||
|
(0xC2, 0x37),
|
||||||
|
(0xC3, 0x80),
|
||||||
|
(0xC4, 0x10),
|
||||||
|
(0xC5, 0x37),
|
||||||
|
(0xC6, 0xA9),
|
||||||
|
(0xC7, 0x41),
|
||||||
|
(0xC8, 0x01),
|
||||||
|
(0xC9, 0xA9),
|
||||||
|
(0xCA, 0x41),
|
||||||
|
(0xCB, 0x01),
|
||||||
|
(0xD0, 0x91),
|
||||||
|
(0xD1, 0x68),
|
||||||
|
(0xD2, 0x68),
|
||||||
|
(0xF5, 0x00, 0xA5),
|
||||||
|
(0xDD, 0x4F),
|
||||||
|
(0xDE, 0x4F),
|
||||||
|
(0xF1, 0x10),
|
||||||
|
(0xF0, 0x00),
|
||||||
|
(0xF0, 0x02),
|
||||||
|
(
|
||||||
|
0xE0,
|
||||||
|
0xF0,
|
||||||
|
0x0A,
|
||||||
|
0x10,
|
||||||
|
0x09,
|
||||||
|
0x09,
|
||||||
|
0x36,
|
||||||
|
0x35,
|
||||||
|
0x33,
|
||||||
|
0x4A,
|
||||||
|
0x29,
|
||||||
|
0x15,
|
||||||
|
0x15,
|
||||||
|
0x2E,
|
||||||
|
0x34,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xE1,
|
||||||
|
0xF0,
|
||||||
|
0x0A,
|
||||||
|
0x0F,
|
||||||
|
0x08,
|
||||||
|
0x08,
|
||||||
|
0x05,
|
||||||
|
0x34,
|
||||||
|
0x33,
|
||||||
|
0x4A,
|
||||||
|
0x39,
|
||||||
|
0x15,
|
||||||
|
0x15,
|
||||||
|
0x2D,
|
||||||
|
0x33,
|
||||||
|
),
|
||||||
|
(0xF0, 0x10),
|
||||||
|
(0xF3, 0x10),
|
||||||
|
(0xE0, 0x07),
|
||||||
|
(0xE1, 0x00),
|
||||||
|
(0xE2, 0x00),
|
||||||
|
(0xE3, 0x00),
|
||||||
|
(0xE4, 0xE0),
|
||||||
|
(0xE5, 0x06),
|
||||||
|
(0xE6, 0x21),
|
||||||
|
(0xE7, 0x01),
|
||||||
|
(0xE8, 0x05),
|
||||||
|
(0xE9, 0x02),
|
||||||
|
(0xEA, 0xDA),
|
||||||
|
(0xEB, 0x00),
|
||||||
|
(0xEC, 0x00),
|
||||||
|
(0xED, 0x0F),
|
||||||
|
(0xEE, 0x00),
|
||||||
|
(0xEF, 0x00),
|
||||||
|
(0xF8, 0x00),
|
||||||
|
(0xF9, 0x00),
|
||||||
|
(0xFA, 0x00),
|
||||||
|
(0xFB, 0x00),
|
||||||
|
(0xFC, 0x00),
|
||||||
|
(0xFD, 0x00),
|
||||||
|
(0xFE, 0x00),
|
||||||
|
(0xFF, 0x00),
|
||||||
|
(0x60, 0x40),
|
||||||
|
(0x61, 0x04),
|
||||||
|
(0x62, 0x00),
|
||||||
|
(0x63, 0x42),
|
||||||
|
(0x64, 0xD9),
|
||||||
|
(0x65, 0x00),
|
||||||
|
(0x66, 0x00),
|
||||||
|
(0x67, 0x00),
|
||||||
|
(0x68, 0x00),
|
||||||
|
(0x69, 0x00),
|
||||||
|
(0x6A, 0x00),
|
||||||
|
(0x6B, 0x00),
|
||||||
|
(0x70, 0x40),
|
||||||
|
(0x71, 0x03),
|
||||||
|
(0x72, 0x00),
|
||||||
|
(0x73, 0x42),
|
||||||
|
(0x74, 0xD8),
|
||||||
|
(0x75, 0x00),
|
||||||
|
(0x76, 0x00),
|
||||||
|
(0x77, 0x00),
|
||||||
|
(0x78, 0x00),
|
||||||
|
(0x79, 0x00),
|
||||||
|
(0x7A, 0x00),
|
||||||
|
(0x7B, 0x00),
|
||||||
|
(0x80, 0x48),
|
||||||
|
(0x81, 0x00),
|
||||||
|
(0x82, 0x06),
|
||||||
|
(0x83, 0x02),
|
||||||
|
(0x84, 0xD6),
|
||||||
|
(0x85, 0x04),
|
||||||
|
(0x86, 0x00),
|
||||||
|
(0x87, 0x00),
|
||||||
|
(0x88, 0x48),
|
||||||
|
(0x89, 0x00),
|
||||||
|
(0x8A, 0x08),
|
||||||
|
(0x8B, 0x02),
|
||||||
|
(0x8C, 0xD8),
|
||||||
|
(0x8D, 0x04),
|
||||||
|
(0x8E, 0x00),
|
||||||
|
(0x8F, 0x00),
|
||||||
|
(0x90, 0x48),
|
||||||
|
(0x91, 0x00),
|
||||||
|
(0x92, 0x0A),
|
||||||
|
(0x93, 0x02),
|
||||||
|
(0x94, 0xDA),
|
||||||
|
(0x95, 0x04),
|
||||||
|
(0x96, 0x00),
|
||||||
|
(0x97, 0x00),
|
||||||
|
(0x98, 0x48),
|
||||||
|
(0x99, 0x00),
|
||||||
|
(0x9A, 0x0C),
|
||||||
|
(0x9B, 0x02),
|
||||||
|
(0x9C, 0xDC),
|
||||||
|
(0x9D, 0x04),
|
||||||
|
(0x9E, 0x00),
|
||||||
|
(0x9F, 0x00),
|
||||||
|
(0xA0, 0x48),
|
||||||
|
(0xA1, 0x00),
|
||||||
|
(0xA2, 0x05),
|
||||||
|
(0xA3, 0x02),
|
||||||
|
(0xA4, 0xD5),
|
||||||
|
(0xA5, 0x04),
|
||||||
|
(0xA6, 0x00),
|
||||||
|
(0xA7, 0x00),
|
||||||
|
(0xA8, 0x48),
|
||||||
|
(0xA9, 0x00),
|
||||||
|
(0xAA, 0x07),
|
||||||
|
(0xAB, 0x02),
|
||||||
|
(0xAC, 0xD7),
|
||||||
|
(0xAD, 0x04),
|
||||||
|
(0xAE, 0x00),
|
||||||
|
(0xAF, 0x00),
|
||||||
|
(0xB0, 0x48),
|
||||||
|
(0xB1, 0x00),
|
||||||
|
(0xB2, 0x09),
|
||||||
|
(0xB3, 0x02),
|
||||||
|
(0xB4, 0xD9),
|
||||||
|
(0xB5, 0x04),
|
||||||
|
(0xB6, 0x00),
|
||||||
|
(0xB7, 0x00),
|
||||||
|
(0xB8, 0x48),
|
||||||
|
(0xB9, 0x00),
|
||||||
|
(0xBA, 0x0B),
|
||||||
|
(0xBB, 0x02),
|
||||||
|
(0xBC, 0xDB),
|
||||||
|
(0xBD, 0x04),
|
||||||
|
(0xBE, 0x00),
|
||||||
|
(0xBF, 0x00),
|
||||||
|
(0xC0, 0x10),
|
||||||
|
(0xC1, 0x47),
|
||||||
|
(0xC2, 0x56),
|
||||||
|
(0xC3, 0x65),
|
||||||
|
(0xC4, 0x74),
|
||||||
|
(0xC5, 0x88),
|
||||||
|
(0xC6, 0x99),
|
||||||
|
(0xC7, 0x01),
|
||||||
|
(0xC8, 0xBB),
|
||||||
|
(0xC9, 0xAA),
|
||||||
|
(0xD0, 0x10),
|
||||||
|
(0xD1, 0x47),
|
||||||
|
(0xD2, 0x56),
|
||||||
|
(0xD3, 0x65),
|
||||||
|
(0xD4, 0x74),
|
||||||
|
(0xD5, 0x88),
|
||||||
|
(0xD6, 0x99),
|
||||||
|
(0xD7, 0x01),
|
||||||
|
(0xD8, 0xBB),
|
||||||
|
(0xD9, 0xAA),
|
||||||
|
(0xF3, 0x01),
|
||||||
|
(0xF0, 0x00),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
models = {}
|
models = {}
|
||||||
|
@@ -119,8 +119,8 @@ async def to_code(config: ConfigType) -> None:
|
|||||||
cg.add_platformio_option(
|
cg.add_platformio_option(
|
||||||
"platform_packages",
|
"platform_packages",
|
||||||
[
|
[
|
||||||
"platformio/framework-zephyr@https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v2.6.1-4.zip",
|
"platformio/framework-zephyr@https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v2.6.1-7.zip",
|
||||||
"platformio/toolchain-gccarmnoneeabi@https://github.com/tomaszduda23/toolchain-sdk-ng/archive/refs/tags/v0.16.1-1.zip",
|
"platformio/toolchain-gccarmnoneeabi@https://github.com/tomaszduda23/toolchain-sdk-ng/archive/refs/tags/v0.17.4-0.zip",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import modbus, sensor
|
from esphome.components import modbus, sensor
|
||||||
from esphome.components.atm90e32.sensor import CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C
|
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ACTIVE_POWER,
|
CONF_ACTIVE_POWER,
|
||||||
@@ -12,7 +11,10 @@ from esphome.const import (
|
|||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_IMPORT_ACTIVE_ENERGY,
|
CONF_IMPORT_ACTIVE_ENERGY,
|
||||||
CONF_IMPORT_REACTIVE_ENERGY,
|
CONF_IMPORT_REACTIVE_ENERGY,
|
||||||
|
CONF_PHASE_A,
|
||||||
CONF_PHASE_ANGLE,
|
CONF_PHASE_ANGLE,
|
||||||
|
CONF_PHASE_B,
|
||||||
|
CONF_PHASE_C,
|
||||||
CONF_POWER_FACTOR,
|
CONF_POWER_FACTOR,
|
||||||
CONF_REACTIVE_POWER,
|
CONF_REACTIVE_POWER,
|
||||||
CONF_TOTAL_POWER,
|
CONF_TOTAL_POWER,
|
||||||
|
@@ -52,9 +52,9 @@ def default_url(config: ConfigType) -> ConfigType:
|
|||||||
config = config.copy()
|
config = config.copy()
|
||||||
if config[CONF_VERSION] == 1:
|
if config[CONF_VERSION] == 1:
|
||||||
if CONF_CSS_URL not in config:
|
if CONF_CSS_URL not in config:
|
||||||
config[CONF_CSS_URL] = "https://esphome.io/_static/webserver-v1.min.css"
|
config[CONF_CSS_URL] = "https://oi.esphome.io/v1/webserver-v1.min.css"
|
||||||
if CONF_JS_URL not in config:
|
if CONF_JS_URL not in config:
|
||||||
config[CONF_JS_URL] = "https://esphome.io/_static/webserver-v1.min.js"
|
config[CONF_JS_URL] = "https://oi.esphome.io/v1/webserver-v1.min.js"
|
||||||
if config[CONF_VERSION] == 2:
|
if config[CONF_VERSION] == 2:
|
||||||
if CONF_CSS_URL not in config:
|
if CONF_CSS_URL not in config:
|
||||||
config[CONF_CSS_URL] = ""
|
config[CONF_CSS_URL] = ""
|
||||||
|
@@ -173,14 +173,14 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|||||||
|
|
||||||
#if USE_WEBSERVER_VERSION == 1
|
#if USE_WEBSERVER_VERSION == 1
|
||||||
/** Set the URL to the CSS <link> that's sent to each client. Defaults to
|
/** Set the URL to the CSS <link> that's sent to each client. Defaults to
|
||||||
* https://esphome.io/_static/webserver-v1.min.css
|
* https://oi.esphome.io/v1/webserver-v1.min.css
|
||||||
*
|
*
|
||||||
* @param css_url The url to the web server stylesheet.
|
* @param css_url The url to the web server stylesheet.
|
||||||
*/
|
*/
|
||||||
void set_css_url(const char *css_url);
|
void set_css_url(const char *css_url);
|
||||||
|
|
||||||
/** Set the URL to the script that's embedded in the index page. Defaults to
|
/** Set the URL to the script that's embedded in the index page. Defaults to
|
||||||
* https://esphome.io/_static/webserver-v1.min.js
|
* https://oi.esphome.io/v1/webserver-v1.min.js
|
||||||
*
|
*
|
||||||
* @param js_url The url to the web server script.
|
* @param js_url The url to the web server script.
|
||||||
*/
|
*/
|
||||||
|
@@ -253,7 +253,7 @@ bool AsyncWebServerRequest::authenticate(const char *username, const char *passw
|
|||||||
esp_crypto_base64_encode(reinterpret_cast<uint8_t *>(digest.get()), n, &out,
|
esp_crypto_base64_encode(reinterpret_cast<uint8_t *>(digest.get()), n, &out,
|
||||||
reinterpret_cast<const uint8_t *>(user_info.c_str()), user_info.size());
|
reinterpret_cast<const uint8_t *>(user_info.c_str()), user_info.size());
|
||||||
|
|
||||||
return strncmp(digest.get(), auth_str + auth_prefix_len, auth.value().size() - auth_prefix_len) == 0;
|
return strcmp(digest.get(), auth_str + auth_prefix_len) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::requestAuthentication(const char *realm) const {
|
void AsyncWebServerRequest::requestAuthentication(const char *realm) const {
|
||||||
|
@@ -1112,8 +1112,8 @@ voltage = float_with_unit("voltage", "(v|V|volt|Volts)?")
|
|||||||
distance = float_with_unit("distance", "(m)")
|
distance = float_with_unit("distance", "(m)")
|
||||||
framerate = float_with_unit("framerate", "(FPS|fps|Fps|FpS|Hz)")
|
framerate = float_with_unit("framerate", "(FPS|fps|Fps|FpS|Hz)")
|
||||||
angle = float_with_unit("angle", "(°|deg)", optional_unit=True)
|
angle = float_with_unit("angle", "(°|deg)", optional_unit=True)
|
||||||
_temperature_c = float_with_unit("temperature", "(°C|° C|°|C)?")
|
_temperature_c = float_with_unit("temperature", "(°C|° C|C|°)?")
|
||||||
_temperature_k = float_with_unit("temperature", "(° K|° K|K)?")
|
_temperature_k = float_with_unit("temperature", "(°K|° K|K)?")
|
||||||
_temperature_f = float_with_unit("temperature", "(°F|° F|F)?")
|
_temperature_f = float_with_unit("temperature", "(°F|° F|F)?")
|
||||||
decibel = float_with_unit("decibel", "(dB|dBm|db|dbm)", optional_unit=True)
|
decibel = float_with_unit("decibel", "(dB|dBm|db|dbm)", optional_unit=True)
|
||||||
pressure = float_with_unit("pressure", "(bar|Bar)", optional_unit=True)
|
pressure = float_with_unit("pressure", "(bar|Bar)", optional_unit=True)
|
||||||
|
@@ -5,6 +5,8 @@
|
|||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/core/preferences.h"
|
#include "esphome/core/preferences.h"
|
||||||
|
#include "esphome/core/scheduler.h"
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -158,7 +160,16 @@ template<typename... Ts> class DelayAction : public Action<Ts...>, public Compon
|
|||||||
void play_complex(Ts... x) override {
|
void play_complex(Ts... x) override {
|
||||||
auto f = std::bind(&DelayAction<Ts...>::play_next_, this, x...);
|
auto f = std::bind(&DelayAction<Ts...>::play_next_, this, x...);
|
||||||
this->num_running_++;
|
this->num_running_++;
|
||||||
this->set_timeout("delay", this->delay_.value(x...), f);
|
|
||||||
|
// If num_running_ > 1, we have multiple instances running in parallel
|
||||||
|
// In single/restart/queued modes, only one instance runs at a time
|
||||||
|
// Parallel mode uses skip_cancel=true to allow multiple delays to coexist
|
||||||
|
// WARNING: This can accumulate delays if scripts are triggered faster than they complete!
|
||||||
|
// Users should set max_runs on parallel scripts to limit concurrent executions.
|
||||||
|
// Issue #10264: This is a workaround for parallel script delays interfering with each other.
|
||||||
|
App.scheduler.set_timer_common_(this, Scheduler::SchedulerItem::TIMEOUT,
|
||||||
|
/* is_static_string= */ true, "delay", this->delay_.value(x...), std::move(f),
|
||||||
|
/* is_retry= */ false, /* skip_cancel= */ this->num_running_ > 1);
|
||||||
}
|
}
|
||||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
|
@@ -85,7 +85,23 @@ class EntityBase {
|
|||||||
// Set has_state - for components that need to manually set this
|
// Set has_state - for components that need to manually set this
|
||||||
void set_has_state(bool state) { this->flags_.has_state = state; }
|
void set_has_state(bool state) { this->flags_.has_state = state; }
|
||||||
|
|
||||||
// Get a unique hash for preferences that includes device_id
|
/**
|
||||||
|
* @brief Get a unique hash for storing preferences/settings for this entity.
|
||||||
|
*
|
||||||
|
* This method returns a hash that uniquely identifies the entity for the purpose of
|
||||||
|
* storing preferences (such as calibration, state, etc.). Unlike get_object_id_hash(),
|
||||||
|
* this hash also incorporates the device_id (if devices are enabled), ensuring uniqueness
|
||||||
|
* across multiple devices that may have entities with the same object_id.
|
||||||
|
*
|
||||||
|
* Use this method when storing or retrieving preferences/settings that should be unique
|
||||||
|
* per device-entity pair. Use get_object_id_hash() when you need a hash that identifies
|
||||||
|
* the entity regardless of the device it belongs to.
|
||||||
|
*
|
||||||
|
* For backward compatibility, if device_id is 0 (the main device), the hash is unchanged
|
||||||
|
* from previous versions, so existing single-device configurations will continue to work.
|
||||||
|
*
|
||||||
|
* @return uint32_t The unique hash for preferences, including device_id if available.
|
||||||
|
*/
|
||||||
uint32_t get_preference_hash() {
|
uint32_t get_preference_hash() {
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
// Combine object_id_hash with device_id to ensure uniqueness across devices
|
// Combine object_id_hash with device_id to ensure uniqueness across devices
|
||||||
|
@@ -65,14 +65,17 @@ static void validate_static_string(const char *name) {
|
|||||||
|
|
||||||
// Common implementation for both timeout and interval
|
// Common implementation for both timeout and interval
|
||||||
void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type type, bool is_static_string,
|
void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type type, bool is_static_string,
|
||||||
const void *name_ptr, uint32_t delay, std::function<void()> func, bool is_retry) {
|
const void *name_ptr, uint32_t delay, std::function<void()> func, bool is_retry,
|
||||||
|
bool skip_cancel) {
|
||||||
// Get the name as const char*
|
// Get the name as const char*
|
||||||
const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
|
const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
|
||||||
|
|
||||||
if (delay == SCHEDULER_DONT_RUN) {
|
if (delay == SCHEDULER_DONT_RUN) {
|
||||||
// Still need to cancel existing timer if name is not empty
|
// Still need to cancel existing timer if name is not empty
|
||||||
LockGuard guard{this->lock_};
|
if (!skip_cancel) {
|
||||||
this->cancel_item_locked_(component, name_cstr, type);
|
LockGuard guard{this->lock_};
|
||||||
|
this->cancel_item_locked_(component, name_cstr, type);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +100,9 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|||||||
if (delay == 0 && type == SchedulerItem::TIMEOUT) {
|
if (delay == 0 && type == SchedulerItem::TIMEOUT) {
|
||||||
// Put in defer queue for guaranteed FIFO execution
|
// Put in defer queue for guaranteed FIFO execution
|
||||||
LockGuard guard{this->lock_};
|
LockGuard guard{this->lock_};
|
||||||
this->cancel_item_locked_(component, name_cstr, type);
|
if (!skip_cancel) {
|
||||||
|
this->cancel_item_locked_(component, name_cstr, type);
|
||||||
|
}
|
||||||
this->defer_queue_.push_back(std::move(item));
|
this->defer_queue_.push_back(std::move(item));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -150,9 +155,11 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If name is provided, do atomic cancel-and-add
|
// If name is provided, do atomic cancel-and-add (unless skip_cancel is true)
|
||||||
// Cancel existing items
|
// Cancel existing items
|
||||||
this->cancel_item_locked_(component, name_cstr, type);
|
if (!skip_cancel) {
|
||||||
|
this->cancel_item_locked_(component, name_cstr, type);
|
||||||
|
}
|
||||||
// Add new item directly to to_add_
|
// Add new item directly to to_add_
|
||||||
// since we have the lock held
|
// since we have the lock held
|
||||||
this->to_add_.push_back(std::move(item));
|
this->to_add_.push_back(std::move(item));
|
||||||
|
@@ -21,8 +21,13 @@ struct RetryArgs;
|
|||||||
void retry_handler(const std::shared_ptr<RetryArgs> &args);
|
void retry_handler(const std::shared_ptr<RetryArgs> &args);
|
||||||
|
|
||||||
class Scheduler {
|
class Scheduler {
|
||||||
// Allow retry_handler to access protected members
|
// Allow retry_handler to access protected members for internal retry mechanism
|
||||||
friend void ::esphome::retry_handler(const std::shared_ptr<RetryArgs> &args);
|
friend void ::esphome::retry_handler(const std::shared_ptr<RetryArgs> &args);
|
||||||
|
// Allow DelayAction to call set_timer_common_ with skip_cancel=true for parallel script delays.
|
||||||
|
// This is needed to fix issue #10264 where parallel scripts with delays interfere with each other.
|
||||||
|
// We use friend instead of a public API because skip_cancel is dangerous - it can cause delays
|
||||||
|
// to accumulate and overload the scheduler if misused.
|
||||||
|
template<typename... Ts> friend class DelayAction;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Public API - accepts std::string for backward compatibility
|
// Public API - accepts std::string for backward compatibility
|
||||||
@@ -184,7 +189,7 @@ class Scheduler {
|
|||||||
|
|
||||||
// Common implementation for both timeout and interval
|
// Common implementation for both timeout and interval
|
||||||
void set_timer_common_(Component *component, SchedulerItem::Type type, bool is_static_string, const void *name_ptr,
|
void set_timer_common_(Component *component, SchedulerItem::Type type, bool is_static_string, const void *name_ptr,
|
||||||
uint32_t delay, std::function<void()> func, bool is_retry = false);
|
uint32_t delay, std::function<void()> func, bool is_retry = false, bool skip_cancel = false);
|
||||||
|
|
||||||
// Common implementation for retry
|
// Common implementation for retry
|
||||||
void set_retry_common_(Component *component, bool is_static_string, const void *name_ptr, uint32_t initial_wait_time,
|
void set_retry_common_(Component *component, bool is_static_string, const void *name_ptr, uint32_t initial_wait_time,
|
||||||
|
@@ -221,8 +221,8 @@ extends = common
|
|||||||
platform = https://github.com/tomaszduda23/platform-nordicnrf52/archive/refs/tags/v10.3.0-1.zip
|
platform = https://github.com/tomaszduda23/platform-nordicnrf52/archive/refs/tags/v10.3.0-1.zip
|
||||||
framework = zephyr
|
framework = zephyr
|
||||||
platform_packages =
|
platform_packages =
|
||||||
platformio/framework-zephyr @ https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v2.6.1-4.zip
|
platformio/framework-zephyr @ https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v2.6.1-7.zip
|
||||||
platformio/toolchain-gccarmnoneeabi@https://github.com/tomaszduda23/toolchain-sdk-ng/archive/refs/tags/v0.16.1-1.zip
|
platformio/toolchain-gccarmnoneeabi@https://github.com/tomaszduda23/toolchain-sdk-ng/archive/refs/tags/v0.17.4-0.zip
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.build_flags}
|
${common.build_flags}
|
||||||
-DUSE_ZEPHYR
|
-DUSE_ZEPHYR
|
||||||
|
5
tests/components/deep_sleep/test.esp32-c6-idf.yaml
Normal file
5
tests/components/deep_sleep/test.esp32-c6-idf.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
wakeup_pin: GPIO4
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
||||||
|
<<: !include common-esp32.yaml
|
5
tests/components/deep_sleep/test.esp32-s2-idf.yaml
Normal file
5
tests/components/deep_sleep/test.esp32-s2-idf.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
wakeup_pin: GPIO4
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
||||||
|
<<: !include common-esp32.yaml
|
5
tests/components/deep_sleep/test.esp32-s3-idf.yaml
Normal file
5
tests/components/deep_sleep/test.esp32-s3-idf.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
wakeup_pin: GPIO4
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
||||||
|
<<: !include common-esp32.yaml
|
@@ -56,10 +56,29 @@ light:
|
|||||||
warm_white_color_temperature: 2000 K
|
warm_white_color_temperature: 2000 K
|
||||||
constant_brightness: true
|
constant_brightness: true
|
||||||
effects:
|
effects:
|
||||||
|
# Use default parameters:
|
||||||
- random:
|
- random:
|
||||||
name: "Random Effect"
|
# Customize parameters - use longer names to potentially trigger buffer issues
|
||||||
|
- random:
|
||||||
|
name: "My Very Slow Random Effect With Long Name"
|
||||||
|
transition_length: 30ms
|
||||||
|
update_interval: 30ms
|
||||||
|
- random:
|
||||||
|
name: "My Fast Random Effect That Changes Quickly"
|
||||||
|
transition_length: 4ms
|
||||||
|
update_interval: 5ms
|
||||||
|
- random:
|
||||||
|
name: "Random Effect With Medium Length Name Here"
|
||||||
transition_length: 100ms
|
transition_length: 100ms
|
||||||
update_interval: 200ms
|
update_interval: 200ms
|
||||||
|
- random:
|
||||||
|
name: "Another Random Effect With Different Parameters"
|
||||||
|
transition_length: 2ms
|
||||||
|
update_interval: 3ms
|
||||||
|
- random:
|
||||||
|
name: "Yet Another Random Effect To Test Memory"
|
||||||
|
transition_length: 15ms
|
||||||
|
update_interval: 20ms
|
||||||
- strobe:
|
- strobe:
|
||||||
name: "Strobe Effect"
|
name: "Strobe Effect"
|
||||||
- pulse:
|
- pulse:
|
||||||
@@ -73,6 +92,17 @@ light:
|
|||||||
red: test_red
|
red: test_red
|
||||||
green: test_green
|
green: test_green
|
||||||
blue: test_blue
|
blue: test_blue
|
||||||
|
effects:
|
||||||
|
# Same random effects to test for cross-contamination
|
||||||
|
- random:
|
||||||
|
- random:
|
||||||
|
name: "RGB Slow Random"
|
||||||
|
transition_length: 20ms
|
||||||
|
update_interval: 25ms
|
||||||
|
- random:
|
||||||
|
name: "RGB Fast Random"
|
||||||
|
transition_length: 2ms
|
||||||
|
update_interval: 3ms
|
||||||
|
|
||||||
- platform: binary
|
- platform: binary
|
||||||
name: "Test Binary Light"
|
name: "Test Binary Light"
|
||||||
|
45
tests/integration/fixtures/parallel_script_delays.yaml
Normal file
45
tests/integration/fixtures/parallel_script_delays.yaml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
esphome:
|
||||||
|
name: test-parallel-delays
|
||||||
|
|
||||||
|
host:
|
||||||
|
|
||||||
|
logger:
|
||||||
|
level: DEBUG
|
||||||
|
|
||||||
|
api:
|
||||||
|
actions:
|
||||||
|
- action: test_parallel_delays
|
||||||
|
then:
|
||||||
|
# Start three parallel script instances with small delays between starts
|
||||||
|
- globals.set:
|
||||||
|
id: instance_counter
|
||||||
|
value: '1'
|
||||||
|
- script.execute: parallel_delay_script
|
||||||
|
- delay: 10ms
|
||||||
|
- globals.set:
|
||||||
|
id: instance_counter
|
||||||
|
value: '2'
|
||||||
|
- script.execute: parallel_delay_script
|
||||||
|
- delay: 10ms
|
||||||
|
- globals.set:
|
||||||
|
id: instance_counter
|
||||||
|
value: '3'
|
||||||
|
- script.execute: parallel_delay_script
|
||||||
|
|
||||||
|
globals:
|
||||||
|
- id: instance_counter
|
||||||
|
type: int
|
||||||
|
initial_value: '0'
|
||||||
|
|
||||||
|
script:
|
||||||
|
- id: parallel_delay_script
|
||||||
|
mode: parallel
|
||||||
|
then:
|
||||||
|
- lambda: !lambda |-
|
||||||
|
int instance = id(instance_counter);
|
||||||
|
ESP_LOGI("TEST", "Parallel script instance %d started", instance);
|
||||||
|
- delay: 1s
|
||||||
|
- lambda: !lambda |-
|
||||||
|
static int completed_counter = 0;
|
||||||
|
completed_counter++;
|
||||||
|
ESP_LOGI("TEST", "Parallel script instance %d completed after delay", completed_counter);
|
@@ -89,3 +89,73 @@ async def test_delay_action_cancellation(
|
|||||||
assert 0.4 < time_from_second_start < 0.6, (
|
assert 0.4 < time_from_second_start < 0.6, (
|
||||||
f"Delay completed {time_from_second_start:.3f}s after second start, expected ~0.5s"
|
f"Delay completed {time_from_second_start:.3f}s after second start, expected ~0.5s"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parallel_script_delays(
|
||||||
|
yaml_config: str,
|
||||||
|
run_compiled: RunCompiledFunction,
|
||||||
|
api_client_connected: APIClientConnectedFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test that parallel scripts with delays don't interfere with each other."""
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
|
||||||
|
# Track script executions
|
||||||
|
script_starts: list[float] = []
|
||||||
|
script_ends: list[float] = []
|
||||||
|
|
||||||
|
# Patterns to match
|
||||||
|
start_pattern = re.compile(r"Parallel script instance \d+ started")
|
||||||
|
end_pattern = re.compile(r"Parallel script instance \d+ completed after delay")
|
||||||
|
|
||||||
|
# Future to track when all scripts have completed
|
||||||
|
all_scripts_completed = loop.create_future()
|
||||||
|
|
||||||
|
def check_output(line: str) -> None:
|
||||||
|
"""Check log output for parallel script messages."""
|
||||||
|
current_time = loop.time()
|
||||||
|
|
||||||
|
if start_pattern.search(line):
|
||||||
|
script_starts.append(current_time)
|
||||||
|
|
||||||
|
if end_pattern.search(line):
|
||||||
|
script_ends.append(current_time)
|
||||||
|
# Check if we have all 3 completions
|
||||||
|
if len(script_ends) == 3 and not all_scripts_completed.done():
|
||||||
|
all_scripts_completed.set_result(True)
|
||||||
|
|
||||||
|
async with (
|
||||||
|
run_compiled(yaml_config, line_callback=check_output),
|
||||||
|
api_client_connected() as client,
|
||||||
|
):
|
||||||
|
# Get services
|
||||||
|
entities, services = await client.list_entities_services()
|
||||||
|
|
||||||
|
# Find our test service
|
||||||
|
test_service = next(
|
||||||
|
(s for s in services if s.name == "test_parallel_delays"), None
|
||||||
|
)
|
||||||
|
assert test_service is not None, "test_parallel_delays service not found"
|
||||||
|
|
||||||
|
# Execute the test - this will start 3 parallel scripts with 1 second delays
|
||||||
|
client.execute_service(test_service, {})
|
||||||
|
|
||||||
|
# Wait for all scripts to complete (should take ~1 second, not 3)
|
||||||
|
await asyncio.wait_for(all_scripts_completed, timeout=2.0)
|
||||||
|
|
||||||
|
# Verify we had 3 starts and 3 ends
|
||||||
|
assert len(script_starts) == 3, (
|
||||||
|
f"Expected 3 script starts, got {len(script_starts)}"
|
||||||
|
)
|
||||||
|
assert len(script_ends) == 3, f"Expected 3 script ends, got {len(script_ends)}"
|
||||||
|
|
||||||
|
# Verify they ran in parallel - all should complete within ~1.5 seconds
|
||||||
|
first_start = min(script_starts)
|
||||||
|
last_end = max(script_ends)
|
||||||
|
total_time = last_end - first_start
|
||||||
|
|
||||||
|
# If running in parallel, total time should be close to 1 second
|
||||||
|
# If they were interfering (running sequentially), it would take 3+ seconds
|
||||||
|
assert total_time < 1.5, (
|
||||||
|
f"Parallel scripts took {total_time:.2f}s total, should be ~1s if running in parallel"
|
||||||
|
)
|
||||||
|
@@ -108,14 +108,51 @@ async def test_light_calls(
|
|||||||
# Wait for flash to end
|
# Wait for flash to end
|
||||||
state = await wait_for_state_change(rgbcw_light.key)
|
state = await wait_for_state_change(rgbcw_light.key)
|
||||||
|
|
||||||
# Test 13: effect only
|
# Test 13: effect only - test all random effects
|
||||||
# First ensure light is on
|
# First ensure light is on
|
||||||
client.light_command(key=rgbcw_light.key, state=True)
|
client.light_command(key=rgbcw_light.key, state=True)
|
||||||
state = await wait_for_state_change(rgbcw_light.key)
|
state = await wait_for_state_change(rgbcw_light.key)
|
||||||
# Now set effect
|
|
||||||
client.light_command(key=rgbcw_light.key, effect="Random Effect")
|
# Test 13a: Default random effect (no name, gets default name "Random")
|
||||||
|
client.light_command(key=rgbcw_light.key, effect="Random")
|
||||||
state = await wait_for_state_change(rgbcw_light.key)
|
state = await wait_for_state_change(rgbcw_light.key)
|
||||||
assert state.effect == "Random Effect"
|
assert state.effect == "Random"
|
||||||
|
|
||||||
|
# Test 13b: Slow random effect with long name
|
||||||
|
client.light_command(
|
||||||
|
key=rgbcw_light.key, effect="My Very Slow Random Effect With Long Name"
|
||||||
|
)
|
||||||
|
state = await wait_for_state_change(rgbcw_light.key)
|
||||||
|
assert state.effect == "My Very Slow Random Effect With Long Name"
|
||||||
|
|
||||||
|
# Test 13c: Fast random effect with long name
|
||||||
|
client.light_command(
|
||||||
|
key=rgbcw_light.key, effect="My Fast Random Effect That Changes Quickly"
|
||||||
|
)
|
||||||
|
state = await wait_for_state_change(rgbcw_light.key)
|
||||||
|
assert state.effect == "My Fast Random Effect That Changes Quickly"
|
||||||
|
|
||||||
|
# Test 13d: Random effect with medium length name
|
||||||
|
client.light_command(
|
||||||
|
key=rgbcw_light.key, effect="Random Effect With Medium Length Name Here"
|
||||||
|
)
|
||||||
|
state = await wait_for_state_change(rgbcw_light.key)
|
||||||
|
assert state.effect == "Random Effect With Medium Length Name Here"
|
||||||
|
|
||||||
|
# Test 13e: Another random effect
|
||||||
|
client.light_command(
|
||||||
|
key=rgbcw_light.key,
|
||||||
|
effect="Another Random Effect With Different Parameters",
|
||||||
|
)
|
||||||
|
state = await wait_for_state_change(rgbcw_light.key)
|
||||||
|
assert state.effect == "Another Random Effect With Different Parameters"
|
||||||
|
|
||||||
|
# Test 13f: Yet another random effect
|
||||||
|
client.light_command(
|
||||||
|
key=rgbcw_light.key, effect="Yet Another Random Effect To Test Memory"
|
||||||
|
)
|
||||||
|
state = await wait_for_state_change(rgbcw_light.key)
|
||||||
|
assert state.effect == "Yet Another Random Effect To Test Memory"
|
||||||
|
|
||||||
# Test 14: stop effect
|
# Test 14: stop effect
|
||||||
client.light_command(key=rgbcw_light.key, effect="None")
|
client.light_command(key=rgbcw_light.key, effect="None")
|
||||||
|
Reference in New Issue
Block a user