mirror of
https://github.com/esphome/esphome.git
synced 2025-04-07 19:30:29 +01:00
commit
573088aadb
@ -72,6 +72,8 @@ void CST226Touchscreen::continue_setup_() {
|
|||||||
if (this->read16_(0xD1F8, buffer, 4)) {
|
if (this->read16_(0xD1F8, buffer, 4)) {
|
||||||
this->x_raw_max_ = buffer[0] + (buffer[1] << 8);
|
this->x_raw_max_ = buffer[0] + (buffer[1] << 8);
|
||||||
this->y_raw_max_ = buffer[2] + (buffer[3] << 8);
|
this->y_raw_max_ = buffer[2] + (buffer[3] << 8);
|
||||||
|
if (this->swap_x_y_)
|
||||||
|
std::swap(this->x_raw_max_, this->y_raw_max_);
|
||||||
} else {
|
} else {
|
||||||
this->x_raw_max_ = this->display_->get_native_width();
|
this->x_raw_max_ = this->display_->get_native_width();
|
||||||
this->y_raw_max_ = this->display_->get_native_height();
|
this->y_raw_max_ = this->display_->get_native_height();
|
||||||
|
@ -34,26 +34,29 @@ void EKTF2232Touchscreen::setup() {
|
|||||||
|
|
||||||
// Get touch resolution
|
// Get touch resolution
|
||||||
uint8_t received[4];
|
uint8_t received[4];
|
||||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
if (this->x_raw_max_ == 0 || this->y_raw_max_ == 0) {
|
||||||
this->write(GET_X_RES, 4);
|
auto err = this->write(GET_X_RES, 4);
|
||||||
if (this->read(received, 4)) {
|
if (err == i2c::ERROR_OK) {
|
||||||
ESP_LOGE(TAG, "Failed to read X resolution!");
|
err = this->read(received, 4);
|
||||||
this->interrupt_pin_->detach_interrupt();
|
if (err == i2c::ERROR_OK) {
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->x_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4);
|
this->x_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4);
|
||||||
|
err = this->write(GET_Y_RES, 4);
|
||||||
|
if (err == i2c::ERROR_OK) {
|
||||||
|
err = this->read(received, 4);
|
||||||
|
if (err == i2c::ERROR_OK) {
|
||||||
|
this->y_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (this->y_raw_max_ == this->y_raw_min_) {
|
}
|
||||||
this->write(GET_Y_RES, 4);
|
}
|
||||||
if (this->read(received, 4)) {
|
if (err != i2c::ERROR_OK) {
|
||||||
ESP_LOGE(TAG, "Failed to read Y resolution!");
|
ESP_LOGE(TAG, "Failed to read calibration values!");
|
||||||
this->interrupt_pin_->detach_interrupt();
|
this->interrupt_pin_->detach_interrupt();
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->y_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4);
|
if (this->swap_x_y_)
|
||||||
|
std::swap(this->x_raw_max_, this->y_raw_max_);
|
||||||
}
|
}
|
||||||
this->set_power_state(true);
|
this->set_power_state(true);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,15 @@ from pathlib import Path
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
import esphome_glyphsets as glyphsets
|
import esphome_glyphsets as glyphsets
|
||||||
from freetype import Face, ft_pixel_mode_grays, ft_pixel_mode_mono
|
|
||||||
|
# pylint: disable=no-name-in-module
|
||||||
|
from freetype import (
|
||||||
|
FT_LOAD_NO_BITMAP,
|
||||||
|
FT_LOAD_RENDER,
|
||||||
|
FT_LOAD_TARGET_MONO,
|
||||||
|
Face,
|
||||||
|
ft_pixel_mode_mono,
|
||||||
|
)
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from esphome import external_files
|
from esphome import external_files
|
||||||
@ -204,7 +212,7 @@ def validate_font_config(config):
|
|||||||
if font.get_char_index(x) != 0
|
if font.get_char_index(x) != 0
|
||||||
]
|
]
|
||||||
|
|
||||||
if font.has_fixed_sizes:
|
if not font.is_scalable:
|
||||||
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
||||||
if not sizes:
|
if not sizes:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
@ -501,17 +509,23 @@ async def to_code(config):
|
|||||||
glyph_args = {}
|
glyph_args = {}
|
||||||
data = []
|
data = []
|
||||||
bpp = config[CONF_BPP]
|
bpp = config[CONF_BPP]
|
||||||
mode = ft_pixel_mode_grays
|
|
||||||
scale = 256 // (1 << bpp)
|
scale = 256 // (1 << bpp)
|
||||||
size = config[CONF_SIZE]
|
size = config[CONF_SIZE]
|
||||||
# create the data array for all glyphs
|
# create the data array for all glyphs
|
||||||
for codepoint in codepoints:
|
for codepoint in codepoints:
|
||||||
font = point_font_map[codepoint]
|
font = point_font_map[codepoint]
|
||||||
format = font.get_format().decode("utf-8")
|
if not font.is_scalable:
|
||||||
if format != "PCF":
|
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
||||||
|
if size in sizes:
|
||||||
|
font.select_size(sizes.index(size))
|
||||||
|
else:
|
||||||
font.set_pixel_sizes(size, 0)
|
font.set_pixel_sizes(size, 0)
|
||||||
font.load_char(codepoint)
|
flags = FT_LOAD_RENDER
|
||||||
font.glyph.render(mode)
|
if bpp != 1:
|
||||||
|
flags |= FT_LOAD_NO_BITMAP
|
||||||
|
else:
|
||||||
|
flags |= FT_LOAD_TARGET_MONO
|
||||||
|
font.load_char(codepoint, flags)
|
||||||
width = font.glyph.bitmap.width
|
width = font.glyph.bitmap.width
|
||||||
height = font.glyph.bitmap.rows
|
height = font.glyph.bitmap.rows
|
||||||
buffer = font.glyph.bitmap.buffer
|
buffer = font.glyph.bitmap.buffer
|
||||||
@ -535,7 +549,7 @@ async def to_code(config):
|
|||||||
pos += 1
|
pos += 1
|
||||||
ascender = pt_to_px(font.size.ascender)
|
ascender = pt_to_px(font.size.ascender)
|
||||||
if ascender == 0:
|
if ascender == 0:
|
||||||
if font.has_fixed_sizes:
|
if not font.is_scalable:
|
||||||
ascender = size
|
ascender = size
|
||||||
else:
|
else:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
@ -585,7 +599,7 @@ async def to_code(config):
|
|||||||
font_height = pt_to_px(base_font.size.height)
|
font_height = pt_to_px(base_font.size.height)
|
||||||
ascender = pt_to_px(base_font.size.ascender)
|
ascender = pt_to_px(base_font.size.ascender)
|
||||||
if font_height == 0:
|
if font_height == 0:
|
||||||
if base_font.has_fixed_sizes:
|
if not base_font.is_scalable:
|
||||||
font_height = size
|
font_height = size
|
||||||
ascender = font_height
|
ascender = font_height
|
||||||
else:
|
else:
|
||||||
|
@ -60,20 +60,25 @@ void GT911Touchscreen::setup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this->x_raw_max_ == 0 || this->y_raw_max_ == 0) {
|
||||||
|
// no calibration? Attempt to read the max values from the touchscreen.
|
||||||
if (err == i2c::ERROR_OK) {
|
if (err == i2c::ERROR_OK) {
|
||||||
err = this->write(GET_MAX_VALUES, 2);
|
err = this->write(GET_MAX_VALUES, 2);
|
||||||
if (err == i2c::ERROR_OK) {
|
if (err == i2c::ERROR_OK) {
|
||||||
err = this->read(data, sizeof(data));
|
err = this->read(data, sizeof(data));
|
||||||
if (err == i2c::ERROR_OK) {
|
if (err == i2c::ERROR_OK) {
|
||||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
|
||||||
this->x_raw_max_ = encode_uint16(data[1], data[0]);
|
this->x_raw_max_ = encode_uint16(data[1], data[0]);
|
||||||
}
|
|
||||||
if (this->y_raw_max_ == this->y_raw_min_) {
|
|
||||||
this->y_raw_max_ = encode_uint16(data[3], data[2]);
|
this->y_raw_max_ = encode_uint16(data[3], data[2]);
|
||||||
|
if (this->swap_x_y_)
|
||||||
|
std::swap(this->x_raw_max_, this->y_raw_max_);
|
||||||
}
|
}
|
||||||
esph_log_d(TAG, "calibration max_x/max_y %d/%d", this->x_raw_max_, this->y_raw_max_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (err != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read calibration values from touchscreen!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (err != i2c::ERROR_OK) {
|
if (err != i2c::ERROR_OK) {
|
||||||
ESP_LOGE(TAG, "Failed to communicate!");
|
ESP_LOGE(TAG, "Failed to communicate!");
|
||||||
|
@ -15,6 +15,7 @@ namespace esphome {
|
|||||||
namespace ld2450 {
|
namespace ld2450 {
|
||||||
|
|
||||||
static const char *const TAG = "ld2450";
|
static const char *const TAG = "ld2450";
|
||||||
|
static const char *const NO_MAC("08:05:04:03:02:01");
|
||||||
static const char *const UNKNOWN_MAC("unknown");
|
static const char *const UNKNOWN_MAC("unknown");
|
||||||
|
|
||||||
// LD2450 UART Serial Commands
|
// LD2450 UART Serial Commands
|
||||||
@ -614,12 +615,12 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
|
|||||||
ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str());
|
ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str());
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
if (this->mac_text_sensor_ != nullptr) {
|
if (this->mac_text_sensor_ != nullptr) {
|
||||||
this->mac_text_sensor_->publish_state(this->mac_);
|
this->mac_text_sensor_->publish_state(this->mac_ == NO_MAC ? UNKNOWN_MAC : this->mac_);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
if (this->bluetooth_switch_ != nullptr) {
|
if (this->bluetooth_switch_ != nullptr) {
|
||||||
this->bluetooth_switch_->publish_state(this->mac_ != UNKNOWN_MAC);
|
this->bluetooth_switch_->publish_state(this->mac_ != NO_MAC);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
@ -56,7 +56,8 @@ const char *media_player_command_to_string(MediaPlayerCommand command) {
|
|||||||
|
|
||||||
void MediaPlayerCall::validate_() {
|
void MediaPlayerCall::validate_() {
|
||||||
if (this->media_url_.has_value()) {
|
if (this->media_url_.has_value()) {
|
||||||
if (this->command_.has_value()) {
|
if (this->command_.has_value() && this->command_.value() != MEDIA_PLAYER_COMMAND_ENQUEUE) {
|
||||||
|
// Don't remove an enqueue command
|
||||||
ESP_LOGW(TAG, "MediaPlayerCall: Setting both command and media_url is not needed.");
|
ESP_LOGW(TAG, "MediaPlayerCall: Setting both command and media_url is not needed.");
|
||||||
this->command_.reset();
|
this->command_.reset();
|
||||||
}
|
}
|
||||||
|
@ -138,77 +138,48 @@ void SpeakerMediaPlayer::watch_media_commands_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MediaCallCommand media_command;
|
MediaCallCommand media_command;
|
||||||
esp_err_t err = ESP_OK;
|
|
||||||
|
|
||||||
if (xQueueReceive(this->media_control_command_queue_, &media_command, 0) == pdTRUE) {
|
if (xQueueReceive(this->media_control_command_queue_, &media_command, 0) == pdTRUE) {
|
||||||
bool new_url = media_command.new_url.has_value() && media_command.new_url.value();
|
|
||||||
bool new_file = media_command.new_file.has_value() && media_command.new_file.value();
|
|
||||||
|
|
||||||
if (new_url || new_file) {
|
|
||||||
bool enqueue = media_command.enqueue.has_value() && media_command.enqueue.value();
|
bool enqueue = media_command.enqueue.has_value() && media_command.enqueue.value();
|
||||||
|
|
||||||
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) {
|
if (media_command.url.has_value() || media_command.file.has_value()) {
|
||||||
// Announcement playlist/pipeline
|
PlaylistItem playlist_item;
|
||||||
|
if (media_command.url.has_value()) {
|
||||||
|
playlist_item.url = *media_command.url.value();
|
||||||
|
delete media_command.url.value();
|
||||||
|
}
|
||||||
|
if (media_command.file.has_value()) {
|
||||||
|
playlist_item.file = media_command.file.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) {
|
||||||
if (!enqueue) {
|
if (!enqueue) {
|
||||||
// Clear the queue and ensure the loaded next item doesn't start playing
|
// Ensure the loaded next item doesn't start playing, clear the queue, start the file, and unpause
|
||||||
this->cancel_timeout("next_ann");
|
this->cancel_timeout("next_ann");
|
||||||
this->announcement_playlist_.clear();
|
this->announcement_playlist_.clear();
|
||||||
}
|
if (media_command.file.has_value()) {
|
||||||
|
|
||||||
PlaylistItem playlist_item;
|
|
||||||
if (new_url) {
|
|
||||||
playlist_item.url = this->announcement_url_;
|
|
||||||
if (!enqueue) {
|
|
||||||
// Not adding to the queue, so directly start playback and internally unpause the pipeline
|
|
||||||
this->announcement_pipeline_->start_url(playlist_item.url.value());
|
|
||||||
this->announcement_pipeline_->set_pause_state(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
playlist_item.file = this->announcement_file_;
|
|
||||||
if (!enqueue) {
|
|
||||||
// Not adding to the queue, so directly start playback and internally unpause the pipeline
|
|
||||||
this->announcement_pipeline_->start_file(playlist_item.file.value());
|
this->announcement_pipeline_->start_file(playlist_item.file.value());
|
||||||
this->announcement_pipeline_->set_pause_state(false);
|
} else if (media_command.url.has_value()) {
|
||||||
|
this->announcement_pipeline_->start_url(playlist_item.url.value());
|
||||||
}
|
}
|
||||||
|
this->announcement_pipeline_->set_pause_state(false);
|
||||||
}
|
}
|
||||||
this->announcement_playlist_.push_back(playlist_item);
|
this->announcement_playlist_.push_back(playlist_item);
|
||||||
} else {
|
} else {
|
||||||
// Media playlist/pipeline
|
|
||||||
|
|
||||||
if (!enqueue) {
|
if (!enqueue) {
|
||||||
// Clear the queue and ensure the loaded next item doesn't start playing
|
// Ensure the loaded next item doesn't start playing, clear the queue, start the file, and unpause
|
||||||
this->cancel_timeout("next_media");
|
this->cancel_timeout("next_media");
|
||||||
this->media_playlist_.clear();
|
this->media_playlist_.clear();
|
||||||
}
|
if (media_command.file.has_value()) {
|
||||||
|
|
||||||
this->is_paused_ = false;
|
|
||||||
PlaylistItem playlist_item;
|
|
||||||
if (new_url) {
|
|
||||||
playlist_item.url = this->media_url_;
|
|
||||||
if (!enqueue) {
|
|
||||||
// Not adding to the queue, so directly start playback and internally unpause the pipeline
|
|
||||||
this->media_pipeline_->start_url(playlist_item.url.value());
|
|
||||||
this->media_pipeline_->set_pause_state(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
playlist_item.file = this->media_file_;
|
|
||||||
if (!enqueue) {
|
|
||||||
// Not adding to the queue, so directly start playback and internally unpause the pipeline
|
|
||||||
this->media_pipeline_->start_file(playlist_item.file.value());
|
this->media_pipeline_->start_file(playlist_item.file.value());
|
||||||
this->media_pipeline_->set_pause_state(false);
|
} else if (media_command.url.has_value()) {
|
||||||
|
this->media_pipeline_->start_url(playlist_item.url.value());
|
||||||
}
|
}
|
||||||
|
this->media_pipeline_->set_pause_state(false);
|
||||||
}
|
}
|
||||||
this->media_playlist_.push_back(playlist_item);
|
this->media_playlist_.push_back(playlist_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "Error starting the audio pipeline: %s", esp_err_to_name(err));
|
|
||||||
this->status_set_error();
|
|
||||||
} else {
|
|
||||||
this->status_clear_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
return; // Don't process the new file play command further
|
return; // Don't process the new file play command further
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,12 +400,10 @@ void SpeakerMediaPlayer::play_file(audio::AudioFile *media_file, bool announceme
|
|||||||
|
|
||||||
MediaCallCommand media_command;
|
MediaCallCommand media_command;
|
||||||
|
|
||||||
media_command.new_file = true;
|
media_command.file = media_file;
|
||||||
if (this->single_pipeline_() || announcement) {
|
if (this->single_pipeline_() || announcement) {
|
||||||
this->announcement_file_ = media_file;
|
|
||||||
media_command.announce = true;
|
media_command.announce = true;
|
||||||
} else {
|
} else {
|
||||||
this->media_file_ = media_file;
|
|
||||||
media_command.announce = false;
|
media_command.announce = false;
|
||||||
}
|
}
|
||||||
media_command.enqueue = enqueue;
|
media_command.enqueue = enqueue;
|
||||||
@ -456,14 +425,8 @@ void SpeakerMediaPlayer::control(const media_player::MediaPlayerCall &call) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (call.get_media_url().has_value()) {
|
if (call.get_media_url().has_value()) {
|
||||||
std::string new_uri = call.get_media_url().value();
|
media_command.url = new std::string(
|
||||||
|
call.get_media_url().value()); // Must be manually deleted after receiving media_command from a queue
|
||||||
media_command.new_url = true;
|
|
||||||
if (this->single_pipeline_() || (call.get_announcement().has_value() && call.get_announcement().value())) {
|
|
||||||
this->announcement_url_ = new_uri;
|
|
||||||
} else {
|
|
||||||
this->media_url_ = new_uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (call.get_command().has_value()) {
|
if (call.get_command().has_value()) {
|
||||||
if (call.get_command().value() == media_player::MEDIA_PLAYER_COMMAND_ENQUEUE) {
|
if (call.get_command().value() == media_player::MEDIA_PLAYER_COMMAND_ENQUEUE) {
|
||||||
|
@ -24,8 +24,8 @@ struct MediaCallCommand {
|
|||||||
optional<media_player::MediaPlayerCommand> command;
|
optional<media_player::MediaPlayerCommand> command;
|
||||||
optional<float> volume;
|
optional<float> volume;
|
||||||
optional<bool> announce;
|
optional<bool> announce;
|
||||||
optional<bool> new_url;
|
optional<std::string *> url; // Must be manually deleted after receiving this struct from a queue
|
||||||
optional<bool> new_file;
|
optional<audio::AudioFile *> file;
|
||||||
optional<bool> enqueue;
|
optional<bool> enqueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -109,15 +109,11 @@ class SpeakerMediaPlayer : public Component, public media_player::MediaPlayer {
|
|||||||
|
|
||||||
optional<media_player::MediaPlayerSupportedFormat> media_format_;
|
optional<media_player::MediaPlayerSupportedFormat> media_format_;
|
||||||
AudioPipelineState media_pipeline_state_{AudioPipelineState::STOPPED};
|
AudioPipelineState media_pipeline_state_{AudioPipelineState::STOPPED};
|
||||||
std::string media_url_{}; // only modified by control function
|
|
||||||
audio::AudioFile *media_file_{}; // only modified by play_file function
|
|
||||||
bool media_repeat_one_{false};
|
bool media_repeat_one_{false};
|
||||||
uint32_t media_playlist_delay_ms_{0};
|
uint32_t media_playlist_delay_ms_{0};
|
||||||
|
|
||||||
optional<media_player::MediaPlayerSupportedFormat> announcement_format_;
|
optional<media_player::MediaPlayerSupportedFormat> announcement_format_;
|
||||||
AudioPipelineState announcement_pipeline_state_{AudioPipelineState::STOPPED};
|
AudioPipelineState announcement_pipeline_state_{AudioPipelineState::STOPPED};
|
||||||
std::string announcement_url_{}; // only modified by control function
|
|
||||||
audio::AudioFile *announcement_file_{}; // only modified by play_file function
|
|
||||||
bool announcement_repeat_one_{false};
|
bool announcement_repeat_one_{false};
|
||||||
uint32_t announcement_playlist_delay_ms_{0};
|
uint32_t announcement_playlist_delay_ms_{0};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Constants used by esphome."""
|
"""Constants used by esphome."""
|
||||||
|
|
||||||
__version__ = "2025.3.1"
|
__version__ = "2025.3.2"
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||||
|
3246
tests/components/font/Tamzen5x9b.bdf
Normal file
3246
tests/components/font/Tamzen5x9b.bdf
Normal file
File diff suppressed because it is too large
Load Diff
@ -43,6 +43,9 @@ font:
|
|||||||
id: default_font
|
id: default_font
|
||||||
- file: $component_dir/x11.pcf
|
- file: $component_dir/x11.pcf
|
||||||
id: pcf_font
|
id: pcf_font
|
||||||
|
- file: $component_dir/Tamzen5x9b.bdf
|
||||||
|
id: bdf_font
|
||||||
|
size: 7
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
scl: ${i2c_scl}
|
scl: ${i2c_scl}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user