mirror of
https://github.com/esphome/esphome.git
synced 2026-09-20 19:48:39 +00:00
Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
@@ -8,14 +8,17 @@ BYTE_ORDER_BIG = "big_endian"
|
||||
|
||||
CONF_COLOR_DEPTH = "color_depth"
|
||||
CONF_CRC_ENABLE = "crc_enable"
|
||||
CONF_DATA_BITS = "data_bits"
|
||||
CONF_DRAW_ROUNDING = "draw_rounding"
|
||||
CONF_ENABLED = "enabled"
|
||||
CONF_IGNORE_NOT_FOUND = "ignore_not_found"
|
||||
CONF_ON_PACKET = "on_packet"
|
||||
CONF_ON_RECEIVE = "on_receive"
|
||||
CONF_ON_STATE_CHANGE = "on_state_change"
|
||||
CONF_PARITY = "parity"
|
||||
CONF_REQUEST_HEADERS = "request_headers"
|
||||
CONF_ROWS = "rows"
|
||||
CONF_STOP_BITS = "stop_bits"
|
||||
CONF_USE_PSRAM = "use_psram"
|
||||
|
||||
ICON_CURRENT_DC = "mdi:current-dc"
|
||||
|
||||
@@ -4,13 +4,20 @@ from esphome.components.esp32 import (
|
||||
VARIANT_ESP32C6,
|
||||
VARIANT_ESP32H2,
|
||||
add_idf_sdkconfig_option,
|
||||
get_esp32_variant,
|
||||
include_builtin_idf_component,
|
||||
only_on_variant,
|
||||
require_vfs_select,
|
||||
)
|
||||
from esphome.components.mdns import MDNSComponent, enable_mdns_storage
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ENABLE_IPV6, CONF_ID, CONF_USE_ADDRESS
|
||||
from esphome.const import (
|
||||
CONF_CHANNEL,
|
||||
CONF_ENABLE_IPV6,
|
||||
CONF_ID,
|
||||
CONF_OUTPUT_POWER,
|
||||
CONF_USE_ADDRESS,
|
||||
)
|
||||
from esphome.core import CORE, TimePeriodMilliseconds
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
@@ -45,6 +52,20 @@ CONF_DEVICE_TYPES = [
|
||||
]
|
||||
|
||||
|
||||
def _validate_txpower(value):
|
||||
if CORE.is_esp32:
|
||||
variant = get_esp32_variant()
|
||||
|
||||
# HW limits: Datasheet section "802.15.4 RF Transmitter (TX) Characteristics"
|
||||
# Further regulatory/soft limit may apply, e.g. by region
|
||||
if variant in (VARIANT_ESP32C6, VARIANT_ESP32C5):
|
||||
return cv.int_range(min=-15, max=20)(value)
|
||||
if variant == VARIANT_ESP32H2:
|
||||
return cv.int_range(min=-24, max=20)(value)
|
||||
|
||||
return value # Unsupported, fail later with clear error
|
||||
|
||||
|
||||
def set_sdkconfig_options(config):
|
||||
# and expose options for using SPI/UART RCPs
|
||||
add_idf_sdkconfig_option("CONFIG_IEEE802154_ENABLED", True)
|
||||
@@ -155,6 +176,10 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_TLV): cv.string_strict,
|
||||
cv.Optional(CONF_USE_ADDRESS): cv.string_strict,
|
||||
cv.Optional(CONF_POLL_PERIOD): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_OUTPUT_POWER): cv.All(
|
||||
cv.decibel,
|
||||
_validate_txpower,
|
||||
),
|
||||
}
|
||||
).extend(_CONNECTION_SCHEMA),
|
||||
cv.has_exactly_one_key(CONF_NETWORK_KEY, CONF_TLV),
|
||||
@@ -197,4 +222,7 @@ async def to_code(config):
|
||||
cg.add(srp.set_mdns(mdns_component))
|
||||
await cg.register_component(srp, config)
|
||||
|
||||
if (output_power := config.get(CONF_OUTPUT_POWER)) is not None:
|
||||
cg.add(ot.set_output_power(output_power))
|
||||
|
||||
set_sdkconfig_options(config)
|
||||
|
||||
@@ -43,6 +43,9 @@ void OpenThreadComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, " Device is configured as Minimal End Device (MED)");
|
||||
}
|
||||
#endif
|
||||
if (this->output_power_.has_value()) {
|
||||
ESP_LOGCONFIG(TAG, " Output power: %" PRId8 "dBm", *this->output_power_);
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenThreadComponent::is_connected() {
|
||||
|
||||
@@ -38,6 +38,7 @@ class OpenThreadComponent : public Component {
|
||||
#if CONFIG_OPENTHREAD_MTD
|
||||
void set_poll_period(uint32_t poll_period) { this->poll_period_ = poll_period; }
|
||||
#endif
|
||||
void set_output_power(int8_t output_power) { this->output_power_ = output_power; }
|
||||
|
||||
protected:
|
||||
std::optional<otIp6Address> get_omr_address_(InstanceLock &lock);
|
||||
@@ -45,6 +46,7 @@ class OpenThreadComponent : public Component {
|
||||
#if CONFIG_OPENTHREAD_MTD
|
||||
uint32_t poll_period_{0};
|
||||
#endif
|
||||
std::optional<int8_t> output_power_{};
|
||||
bool teardown_started_{false};
|
||||
bool teardown_complete_{false};
|
||||
|
||||
|
||||
@@ -135,6 +135,12 @@ void OpenThreadComponent::ot_main() {
|
||||
TRUEFALSE(link_mode_config.mRxOnWhenIdle));
|
||||
#endif
|
||||
|
||||
if (this->output_power_.has_value()) {
|
||||
if (const auto err = otPlatRadioSetTransmitPower(instance, *this->output_power_); err != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to set power: %s", otThreadErrorToString(err));
|
||||
}
|
||||
}
|
||||
|
||||
// Run the main loop
|
||||
#if CONFIG_OPENTHREAD_CLI
|
||||
esp_openthread_cli_create_task();
|
||||
|
||||
@@ -15,6 +15,8 @@ from esphome.const import (
|
||||
CONF_FORMAT,
|
||||
CONF_ID,
|
||||
CONF_NUM_CHANNELS,
|
||||
CONF_ON_TURN_OFF,
|
||||
CONF_ON_TURN_ON,
|
||||
CONF_PATH,
|
||||
CONF_RAW_DATA_ID,
|
||||
CONF_SAMPLE_RATE,
|
||||
@@ -401,6 +403,9 @@ FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
if CONF_ON_TURN_OFF in config or CONF_ON_TURN_ON in config:
|
||||
cg.add_define("USE_SPEAKER_MEDIA_PLAYER_ON_OFF", True)
|
||||
|
||||
var = await media_player.new_media_player(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
|
||||
@@ -51,7 +51,11 @@ static const UBaseType_t ANNOUNCEMENT_PIPELINE_TASK_PRIORITY = 1;
|
||||
static const char *const TAG = "speaker_media_player";
|
||||
|
||||
void SpeakerMediaPlayer::setup() {
|
||||
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
state = media_player::MEDIA_PLAYER_STATE_OFF;
|
||||
#else
|
||||
state = media_player::MEDIA_PLAYER_STATE_IDLE;
|
||||
#endif
|
||||
|
||||
this->media_control_command_queue_ = xQueueCreate(MEDIA_CONTROLS_QUEUE_LENGTH, sizeof(MediaCallCommand));
|
||||
|
||||
@@ -128,6 +132,12 @@ void SpeakerMediaPlayer::watch_media_commands_() {
|
||||
bool enqueue = media_command.enqueue.has_value() && media_command.enqueue.value();
|
||||
|
||||
if (media_command.url.has_value() || media_command.file.has_value()) {
|
||||
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
if (this->state == media_player::MEDIA_PLAYER_STATE_OFF) {
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_ON;
|
||||
publish_state();
|
||||
}
|
||||
#endif
|
||||
PlaylistItem playlist_item;
|
||||
if (media_command.url.has_value()) {
|
||||
playlist_item.url = *media_command.url.value();
|
||||
@@ -184,6 +194,12 @@ void SpeakerMediaPlayer::watch_media_commands_() {
|
||||
if (media_command.command.has_value()) {
|
||||
switch (media_command.command.value()) {
|
||||
case media_player::MEDIA_PLAYER_COMMAND_PLAY:
|
||||
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
if (this->state == media_player::MEDIA_PLAYER_STATE_OFF) {
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_ON;
|
||||
publish_state();
|
||||
}
|
||||
#endif
|
||||
if ((this->media_pipeline_ != nullptr) && (this->is_paused_)) {
|
||||
this->media_pipeline_->set_pause_state(false);
|
||||
}
|
||||
@@ -195,10 +211,26 @@ void SpeakerMediaPlayer::watch_media_commands_() {
|
||||
}
|
||||
this->is_paused_ = true;
|
||||
break;
|
||||
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
case media_player::MEDIA_PLAYER_COMMAND_TURN_ON:
|
||||
if (this->state == media_player::MEDIA_PLAYER_STATE_OFF) {
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_ON;
|
||||
this->publish_state();
|
||||
}
|
||||
break;
|
||||
case media_player::MEDIA_PLAYER_COMMAND_TURN_OFF:
|
||||
this->is_turn_off_ = true;
|
||||
// Intentional Fall-through
|
||||
#endif
|
||||
case media_player::MEDIA_PLAYER_COMMAND_STOP:
|
||||
// Pipelines do not stop immediately after calling the stop command, so confirm its stopped before unpausing.
|
||||
// This avoids an audible short segment playing after receiving the stop command in a paused state.
|
||||
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value()) ||
|
||||
(this->is_turn_off_ && this->announcement_pipeline_state_ != AudioPipelineState::STOPPED)) {
|
||||
#else
|
||||
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) {
|
||||
#endif
|
||||
if (this->announcement_pipeline_ != nullptr) {
|
||||
this->cancel_timeout("next_ann");
|
||||
this->announcement_playlist_.clear();
|
||||
@@ -366,7 +398,13 @@ void SpeakerMediaPlayer::loop() {
|
||||
}
|
||||
} else {
|
||||
if (this->is_paused_) {
|
||||
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
if (this->state != media_player::MEDIA_PLAYER_STATE_OFF) {
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_PAUSED;
|
||||
}
|
||||
#else
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_PAUSED;
|
||||
#endif
|
||||
} else if (this->media_pipeline_state_ == AudioPipelineState::PLAYING) {
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
|
||||
} else if (this->media_pipeline_state_ == AudioPipelineState::STOPPED) {
|
||||
@@ -399,7 +437,13 @@ void SpeakerMediaPlayer::loop() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
if (this->state != media_player::MEDIA_PLAYER_STATE_OFF) {
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
|
||||
}
|
||||
#else
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -409,6 +453,20 @@ void SpeakerMediaPlayer::loop() {
|
||||
this->publish_state();
|
||||
ESP_LOGD(TAG, "State changed to %s", media_player::media_player_state_to_string(this->state));
|
||||
}
|
||||
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
if (this->is_turn_off_ && (this->state == media_player::MEDIA_PLAYER_STATE_PAUSED ||
|
||||
this->state == media_player::MEDIA_PLAYER_STATE_IDLE)) {
|
||||
this->is_turn_off_ = false;
|
||||
if (this->state == media_player::MEDIA_PLAYER_STATE_PAUSED) {
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
|
||||
this->publish_state();
|
||||
ESP_LOGD(TAG, "State changed to %s", media_player::media_player_state_to_string(this->state));
|
||||
}
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_OFF;
|
||||
this->publish_state();
|
||||
ESP_LOGD(TAG, "State changed to %s", media_player::media_player_state_to_string(this->state));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SpeakerMediaPlayer::play_file(audio::AudioFile *media_file, bool announcement, bool enqueue) {
|
||||
@@ -481,6 +539,9 @@ media_player::MediaPlayerTraits SpeakerMediaPlayer::get_traits() {
|
||||
if (!this->single_pipeline_()) {
|
||||
traits.set_supports_pause(true);
|
||||
}
|
||||
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
traits.set_supports_turn_off_on(true);
|
||||
#endif
|
||||
|
||||
if (this->announcement_format_.has_value()) {
|
||||
traits.get_supported_formats().push_back(this->announcement_format_.value());
|
||||
|
||||
@@ -144,6 +144,9 @@ class SpeakerMediaPlayer : public Component,
|
||||
|
||||
bool is_paused_{false};
|
||||
bool is_muted_{false};
|
||||
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
bool is_turn_off_{false};
|
||||
#endif
|
||||
uint8_t unpause_media_remaining_{0};
|
||||
uint8_t unpause_announcement_remaining_{0};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import re
|
||||
|
||||
from esphome import automation, pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.const import CONF_DATA_BITS, CONF_PARITY, CONF_STOP_BITS
|
||||
from esphome.config_helpers import filter_source_files_from_platform
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
@@ -11,7 +12,6 @@ from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_BYTES,
|
||||
CONF_DATA,
|
||||
CONF_DATA_BITS,
|
||||
CONF_DEBUG,
|
||||
CONF_DELIMITER,
|
||||
CONF_DIRECTION,
|
||||
@@ -21,12 +21,10 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_NUMBER,
|
||||
CONF_PARITY,
|
||||
CONF_PORT,
|
||||
CONF_RX_BUFFER_SIZE,
|
||||
CONF_RX_PIN,
|
||||
CONF_SEQUENCE,
|
||||
CONF_STOP_BITS,
|
||||
CONF_TIMEOUT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TX_PIN,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import socket
|
||||
from esphome.components.const import CONF_DATA_BITS, CONF_PARITY, CONF_STOP_BITS
|
||||
from esphome.components.uart import UARTComponent
|
||||
from esphome.components.usb_host import register_usb_client, usb_device_schema
|
||||
import esphome.config_validation as cv
|
||||
@@ -7,12 +8,9 @@ from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_BUFFER_SIZE,
|
||||
CONF_CHANNELS,
|
||||
CONF_DATA_BITS,
|
||||
CONF_DEBUG,
|
||||
CONF_DUMMY_RECEIVER,
|
||||
CONF_ID,
|
||||
CONF_PARITY,
|
||||
CONF_STOP_BITS,
|
||||
)
|
||||
from esphome.cpp_types import Component
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
from esphome.components.const import CONF_DATA_BITS, CONF_PARITY, CONF_STOP_BITS
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_CHANNEL,
|
||||
CONF_DATA_BITS,
|
||||
CONF_ID,
|
||||
CONF_INPUT,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
CONF_OUTPUT,
|
||||
CONF_PARITY,
|
||||
CONF_STOP_BITS,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
|
||||
@@ -280,7 +280,6 @@ CONF_CUSTOM_PRESETS = "custom_presets"
|
||||
CONF_CYCLE = "cycle"
|
||||
CONF_DALLAS_ID = "dallas_id"
|
||||
CONF_DATA = "data"
|
||||
CONF_DATA_BITS = "data_bits"
|
||||
CONF_DATA_PIN = "data_pin"
|
||||
CONF_DATA_PINS = "data_pins"
|
||||
CONF_DATA_RATE = "data_rate"
|
||||
@@ -760,7 +759,6 @@ CONF_PAGE_ID = "page_id"
|
||||
CONF_PAGES = "pages"
|
||||
CONF_PANASONIC = "panasonic"
|
||||
CONF_PARAMETERS = "parameters"
|
||||
CONF_PARITY = "parity"
|
||||
CONF_PASSWORD = "password"
|
||||
CONF_PATH = "path"
|
||||
CONF_PATTERN = "pattern"
|
||||
@@ -963,7 +961,6 @@ CONF_STEP_PIN = "step_pin"
|
||||
CONF_STILL_THRESHOLD = "still_threshold"
|
||||
CONF_STOP = "stop"
|
||||
CONF_STOP_ACTION = "stop_action"
|
||||
CONF_STOP_BITS = "stop_bits"
|
||||
CONF_STORE_BASELINE = "store_baseline"
|
||||
CONF_SUBNET = "subnet"
|
||||
CONF_SUBSCRIBE_QOS = "subscribe_qos"
|
||||
|
||||
@@ -236,6 +236,7 @@
|
||||
#define USE_LWIP_FAST_SELECT
|
||||
#define USE_WAKE_LOOP_THREADSAFE
|
||||
#define USE_SPEAKER
|
||||
#define USE_SPEAKER_MEDIA_PLAYER_ON_OFF
|
||||
#define USE_SPI
|
||||
#define USE_VOICE_ASSISTANT
|
||||
#define USE_WEBSERVER
|
||||
|
||||
@@ -13,3 +13,4 @@ openthread:
|
||||
force_dataset: true
|
||||
use_address: open-thread-test.local
|
||||
poll_period: 20sec
|
||||
output_power: 1dBm
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<<: !include common.yaml
|
||||
|
||||
media_player:
|
||||
- platform: speaker
|
||||
id: speaker_media_player_id
|
||||
announcement_pipeline:
|
||||
speaker: speaker_id
|
||||
buffer_size: 1000000
|
||||
volume_increment: 0.02
|
||||
volume_max: 0.95
|
||||
volume_min: 0.0
|
||||
task_stack_in_psram: true
|
||||
on_turn_on:
|
||||
then:
|
||||
- logger.log: "Turn On Media Player"
|
||||
on_turn_off:
|
||||
then:
|
||||
- logger.log: "Turn Off Media Player"
|
||||
@@ -0,0 +1,9 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO16
|
||||
sda_pin: GPIO17
|
||||
i2s_bclk_pin: GPIO27
|
||||
i2s_lrclk_pin: GPIO26
|
||||
i2s_mclk_pin: GPIO25
|
||||
i2s_dout_pin: GPIO23
|
||||
|
||||
<<: !include common-media_player_off_on.yaml
|
||||
@@ -1,6 +1,7 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
from esphome.components.const import CONF_DATA_BITS, CONF_PARITY, CONF_STOP_BITS
|
||||
from esphome.components.uart import (
|
||||
CONF_RX_FULL_THRESHOLD,
|
||||
CONF_RX_TIMEOUT,
|
||||
@@ -12,14 +13,11 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_DATA,
|
||||
CONF_DATA_BITS,
|
||||
CONF_DEBUG,
|
||||
CONF_DELAY,
|
||||
CONF_ID,
|
||||
CONF_INTERVAL,
|
||||
CONF_PARITY,
|
||||
CONF_RX_BUFFER_SIZE,
|
||||
CONF_STOP_BITS,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome.core import ID
|
||||
|
||||
Reference in New Issue
Block a user