mirror of
https://github.com/esphome/esphome.git
synced 2025-04-09 12:20:30 +01:00
commit
584c5bd5be
@ -555,10 +555,10 @@ void Display::get_text_bounds(int x, int y, const char *text, BaseFont *font, Te
|
||||
|
||||
switch (x_align) {
|
||||
case TextAlign::RIGHT:
|
||||
*x1 = x - *width;
|
||||
*x1 = x - *width - x_offset;
|
||||
break;
|
||||
case TextAlign::CENTER_HORIZONTAL:
|
||||
*x1 = x - (*width) / 2;
|
||||
*x1 = x - (*width + x_offset) / 2;
|
||||
break;
|
||||
case TextAlign::LEFT:
|
||||
default:
|
||||
|
@ -1,7 +1,9 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.switch import Switch, new_switch, switch_schema
|
||||
from esphome.components.switch import Switch, register_switch, switch_schema
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.cpp_types import Component
|
||||
|
||||
from ..defines import CONF_WIDGET, literal
|
||||
from ..lvcode import (
|
||||
@ -18,7 +20,7 @@ from ..lvcode import (
|
||||
from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t, lvgl_ns
|
||||
from ..widgets import get_widgets, wait_for_widgets
|
||||
|
||||
LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch)
|
||||
LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch, Component)
|
||||
CONFIG_SCHEMA = switch_schema(LVGLSwitch).extend(
|
||||
{
|
||||
cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t),
|
||||
@ -27,21 +29,24 @@ CONFIG_SCHEMA = switch_schema(LVGLSwitch).extend(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
switch = await new_switch(config)
|
||||
widget = await get_widgets(config, CONF_WIDGET)
|
||||
widget = widget[0]
|
||||
await wait_for_widgets()
|
||||
async with LambdaContext(EVENT_ARG) as checked_ctx:
|
||||
checked_ctx.add(switch.publish_state(widget.get_value()))
|
||||
switch_id = MockObj(config[CONF_ID], "->")
|
||||
v = literal("v")
|
||||
async with LambdaContext([(cg.bool_, "v")]) as control:
|
||||
with LvConditional(MockObj("v")) as cond:
|
||||
with LvConditional(v) as cond:
|
||||
widget.add_state(LV_STATE.CHECKED)
|
||||
cond.else_()
|
||||
widget.clear_state(LV_STATE.CHECKED)
|
||||
lv.event_send(widget.obj, API_EVENT, cg.nullptr)
|
||||
control.add(switch.publish_state(literal("v")))
|
||||
control.add(switch_id.publish_state(v))
|
||||
switch = cg.new_Pvariable(config[CONF_ID], await control.get_lambda())
|
||||
await cg.register_component(switch, config)
|
||||
await register_switch(switch, config)
|
||||
async with LambdaContext(EVENT_ARG) as checked_ctx:
|
||||
checked_ctx.add(switch.publish_state(widget.get_value()))
|
||||
async with LvContext() as ctx:
|
||||
lv_add(switch.set_control_lambda(await control.get_lambda()))
|
||||
ctx.add(
|
||||
lvgl_static.add_event_cb(
|
||||
widget.obj,
|
||||
|
@ -10,26 +10,15 @@
|
||||
namespace esphome {
|
||||
namespace lvgl {
|
||||
|
||||
class LVGLSwitch : public switch_::Switch {
|
||||
class LVGLSwitch : public switch_::Switch, public Component {
|
||||
public:
|
||||
void set_control_lambda(std::function<void(bool)> state_lambda) {
|
||||
this->state_lambda_ = std::move(state_lambda);
|
||||
if (this->initial_state_.has_value()) {
|
||||
this->state_lambda_(this->initial_state_.value());
|
||||
this->initial_state_.reset();
|
||||
}
|
||||
}
|
||||
LVGLSwitch(std::function<void(bool)> state_lambda) : state_lambda_(std::move(state_lambda)) {}
|
||||
|
||||
void setup() override { this->write_state(this->get_initial_state_with_restore_mode().value_or(false)); }
|
||||
|
||||
protected:
|
||||
void write_state(bool value) override {
|
||||
if (this->state_lambda_ != nullptr) {
|
||||
this->state_lambda_(value);
|
||||
} else {
|
||||
this->initial_state_ = value;
|
||||
}
|
||||
}
|
||||
void write_state(bool value) override { this->state_lambda_(value); }
|
||||
std::function<void(bool)> state_lambda_{};
|
||||
optional<bool> initial_state_{};
|
||||
};
|
||||
|
||||
} // namespace lvgl
|
||||
|
@ -91,7 +91,7 @@ async def to_code(config):
|
||||
add_idf_component(
|
||||
name="mdns",
|
||||
repo="https://github.com/espressif/esp-protocols.git",
|
||||
ref="mdns-v1.8.0",
|
||||
ref="mdns-v1.8.2",
|
||||
path="components/mdns",
|
||||
)
|
||||
|
||||
|
@ -100,7 +100,7 @@ void SpeakerMediaPlayer::setup() {
|
||||
|
||||
if (!this->single_pipeline_()) {
|
||||
this->media_pipeline_ = make_unique<AudioPipeline>(this->media_speaker_, this->buffer_size_,
|
||||
this->task_stack_in_psram_, "ann", MEDIA_PIPELINE_TASK_PRIORITY);
|
||||
this->task_stack_in_psram_, "med", MEDIA_PIPELINE_TASK_PRIORITY);
|
||||
|
||||
if (this->media_pipeline_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to create media pipeline");
|
||||
@ -170,12 +170,28 @@ void SpeakerMediaPlayer::watch_media_commands_() {
|
||||
// Ensure the loaded next item doesn't start playing, clear the queue, start the file, and unpause
|
||||
this->cancel_timeout("next_media");
|
||||
this->media_playlist_.clear();
|
||||
if (media_command.file.has_value()) {
|
||||
this->media_pipeline_->start_file(playlist_item.file.value());
|
||||
} else if (media_command.url.has_value()) {
|
||||
this->media_pipeline_->start_url(playlist_item.url.value());
|
||||
if (this->is_paused_) {
|
||||
// If paused, stop the media pipeline and unpause it after confirming its stopped. This avoids playing a
|
||||
// short segment of the paused file before starting the new one.
|
||||
this->media_pipeline_->stop();
|
||||
this->set_retry("unpause_med", 50, 3, [this](const uint8_t remaining_attempts) {
|
||||
if (this->media_pipeline_state_ == AudioPipelineState::STOPPED) {
|
||||
this->media_pipeline_->set_pause_state(false);
|
||||
this->is_paused_ = false;
|
||||
return RetryResult::DONE;
|
||||
}
|
||||
return RetryResult::RETRY;
|
||||
});
|
||||
} else {
|
||||
// Not paused, just directly start the file
|
||||
if (media_command.file.has_value()) {
|
||||
this->media_pipeline_->start_file(playlist_item.file.value());
|
||||
} else if (media_command.url.has_value()) {
|
||||
this->media_pipeline_->start_url(playlist_item.url.value());
|
||||
}
|
||||
this->media_pipeline_->set_pause_state(false);
|
||||
this->is_paused_ = false;
|
||||
}
|
||||
this->media_pipeline_->set_pause_state(false);
|
||||
}
|
||||
this->media_playlist_.push_back(playlist_item);
|
||||
}
|
||||
@ -203,19 +219,37 @@ void SpeakerMediaPlayer::watch_media_commands_() {
|
||||
this->is_paused_ = true;
|
||||
break;
|
||||
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.
|
||||
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) {
|
||||
if (this->announcement_pipeline_ != nullptr) {
|
||||
this->cancel_timeout("next_ann");
|
||||
this->announcement_playlist_.clear();
|
||||
this->announcement_pipeline_->stop();
|
||||
this->set_retry("unpause_ann", 50, 3, [this](const uint8_t remaining_attempts) {
|
||||
if (this->announcement_pipeline_state_ == AudioPipelineState::STOPPED) {
|
||||
this->announcement_pipeline_->set_pause_state(false);
|
||||
return RetryResult::DONE;
|
||||
}
|
||||
return RetryResult::RETRY;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (this->media_pipeline_ != nullptr) {
|
||||
this->cancel_timeout("next_media");
|
||||
this->media_playlist_.clear();
|
||||
this->media_pipeline_->stop();
|
||||
this->set_retry("unpause_med", 50, 3, [this](const uint8_t remaining_attempts) {
|
||||
if (this->media_pipeline_state_ == AudioPipelineState::STOPPED) {
|
||||
this->media_pipeline_->set_pause_state(false);
|
||||
this->is_paused_ = false;
|
||||
return RetryResult::DONE;
|
||||
}
|
||||
return RetryResult::RETRY;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case media_player::MEDIA_PLAYER_COMMAND_TOGGLE:
|
||||
if (this->media_pipeline_ != nullptr) {
|
||||
@ -332,11 +366,11 @@ void SpeakerMediaPlayer::loop() {
|
||||
}
|
||||
|
||||
if (timeout_ms > 0) {
|
||||
// Pause pipeline internally to facilitiate delay between items
|
||||
// Pause pipeline internally to facilitate the delay between items
|
||||
this->announcement_pipeline_->set_pause_state(true);
|
||||
// Internally unpause the pipeline after the delay between playlist items
|
||||
this->set_timeout("next_ann", timeout_ms,
|
||||
[this]() { this->announcement_pipeline_->set_pause_state(this->is_paused_); });
|
||||
// Internally unpause the pipeline after the delay between playlist items. Announcements do not follow the
|
||||
// media player's pause state.
|
||||
this->set_timeout("next_ann", timeout_ms, [this]() { this->announcement_pipeline_->set_pause_state(false); });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -372,9 +406,10 @@ void SpeakerMediaPlayer::loop() {
|
||||
}
|
||||
|
||||
if (timeout_ms > 0) {
|
||||
// Pause pipeline internally to facilitiate delay between items
|
||||
// Pause pipeline internally to facilitate the delay between items
|
||||
this->media_pipeline_->set_pause_state(true);
|
||||
// Internally unpause the pipeline after the delay between playlist items
|
||||
// Internally unpause the pipeline after the delay between playlist items, if the media player state is
|
||||
// not paused.
|
||||
this->set_timeout("next_media", timeout_ms,
|
||||
[this]() { this->media_pipeline_->set_pause_state(this->is_paused_); });
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""Constants used by esphome."""
|
||||
|
||||
__version__ = "2025.3.2"
|
||||
__version__ = "2025.3.3"
|
||||
|
||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||
|
@ -7,7 +7,7 @@ dependencies:
|
||||
version: v2.0.9
|
||||
mdns:
|
||||
git: https://github.com/espressif/esp-protocols.git
|
||||
version: mdns-v1.8.0
|
||||
version: mdns-v1.8.2
|
||||
path: components/mdns
|
||||
rules:
|
||||
- if: "idf_version >=5.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user