Merge pull request #5548 from esphome/bump-2023.10.0b3

2023.10.0b3
This commit is contained in:
Jesse Hills 2023-10-17 20:49:16 +13:00 committed by GitHub
commit 1a44c6487e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 135 additions and 28 deletions

View file

@ -1459,6 +1459,8 @@ enum VoiceAssistantEvent {
VOICE_ASSISTANT_WAKE_WORD_END = 10; VOICE_ASSISTANT_WAKE_WORD_END = 10;
VOICE_ASSISTANT_STT_VAD_START = 11; VOICE_ASSISTANT_STT_VAD_START = 11;
VOICE_ASSISTANT_STT_VAD_END = 12; VOICE_ASSISTANT_STT_VAD_END = 12;
VOICE_ASSISTANT_TTS_STREAM_START = 98;
VOICE_ASSISTANT_TTS_STREAM_END = 99;
} }
message VoiceAssistantEventData { message VoiceAssistantEventData {

View file

@ -452,6 +452,10 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
return "VOICE_ASSISTANT_STT_VAD_START"; return "VOICE_ASSISTANT_STT_VAD_START";
case enums::VOICE_ASSISTANT_STT_VAD_END: case enums::VOICE_ASSISTANT_STT_VAD_END:
return "VOICE_ASSISTANT_STT_VAD_END"; return "VOICE_ASSISTANT_STT_VAD_END";
case enums::VOICE_ASSISTANT_TTS_STREAM_START:
return "VOICE_ASSISTANT_TTS_STREAM_START";
case enums::VOICE_ASSISTANT_TTS_STREAM_END:
return "VOICE_ASSISTANT_TTS_STREAM_END";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }

View file

@ -184,6 +184,8 @@ enum VoiceAssistantEvent : uint32_t {
VOICE_ASSISTANT_WAKE_WORD_END = 10, VOICE_ASSISTANT_WAKE_WORD_END = 10,
VOICE_ASSISTANT_STT_VAD_START = 11, VOICE_ASSISTANT_STT_VAD_START = 11,
VOICE_ASSISTANT_STT_VAD_END = 12, VOICE_ASSISTANT_STT_VAD_END = 12,
VOICE_ASSISTANT_TTS_STREAM_START = 98,
VOICE_ASSISTANT_TTS_STREAM_END = 99,
}; };
enum AlarmControlPanelState : uint32_t { enum AlarmControlPanelState : uint32_t {
ALARM_STATE_DISARMED = 0, ALARM_STATE_DISARMED = 0,

View file

@ -107,6 +107,7 @@ void ESP32ImprovComponent::loop() {
break; break;
} }
case improv::STATE_AUTHORIZED: { case improv::STATE_AUTHORIZED: {
#ifdef USE_BINARY_SENSOR
if (this->authorizer_ != nullptr) { if (this->authorizer_ != nullptr) {
if (now - this->authorized_start_ > this->authorized_duration_) { if (now - this->authorized_start_ > this->authorized_duration_) {
ESP_LOGD(TAG, "Authorization timeout"); ESP_LOGD(TAG, "Authorization timeout");
@ -114,6 +115,7 @@ void ESP32ImprovComponent::loop() {
return; return;
} }
} }
#endif
if (!this->check_identify_()) { if (!this->check_identify_()) {
this->set_status_indicator_state_((now % 1000) < 500); this->set_status_indicator_state_((now % 1000) < 500);
} }
@ -290,8 +292,10 @@ void ESP32ImprovComponent::process_incoming_data_() {
void ESP32ImprovComponent::on_wifi_connect_timeout_() { void ESP32ImprovComponent::on_wifi_connect_timeout_() {
this->set_error_(improv::ERROR_UNABLE_TO_CONNECT); this->set_error_(improv::ERROR_UNABLE_TO_CONNECT);
this->set_state_(improv::STATE_AUTHORIZED); this->set_state_(improv::STATE_AUTHORIZED);
#ifdef USE_BINARY_SENSOR
if (this->authorizer_ != nullptr) if (this->authorizer_ != nullptr)
this->authorized_start_ = millis(); this->authorized_start_ = millis();
#endif
ESP_LOGW(TAG, "Timed out trying to connect to given WiFi network"); ESP_LOGW(TAG, "Timed out trying to connect to given WiFi network");
wifi::global_wifi_component->clear_sta(); wifi::global_wifi_component->clear_sta();
} }

View file

@ -8,12 +8,15 @@ from esphome.const import (
CONF_CHANNEL, CONF_CHANNEL,
CONF_SPEED, CONF_SPEED,
CONF_DIRECTION, CONF_DIRECTION,
CONF_ADDRESS,
) )
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
CODEOWNERS = ["@max246"] CODEOWNERS = ["@max246"]
MULTI_CONF = True
grove_tb6612fng_ns = cg.esphome_ns.namespace("grove_tb6612fng") grove_tb6612fng_ns = cg.esphome_ns.namespace("grove_tb6612fng")
GROVE_TB6612FNG = grove_tb6612fng_ns.class_( GROVE_TB6612FNG = grove_tb6612fng_ns.class_(
"GroveMotorDriveTB6612FNG", cg.Component, i2c.I2CDevice "GroveMotorDriveTB6612FNG", cg.Component, i2c.I2CDevice
@ -33,6 +36,9 @@ GROVETB6612FNGMotorStandbyAction = grove_tb6612fng_ns.class_(
GROVETB6612FNGMotorNoStandbyAction = grove_tb6612fng_ns.class_( GROVETB6612FNGMotorNoStandbyAction = grove_tb6612fng_ns.class_(
"GROVETB6612FNGMotorNoStandbyAction", automation.Action "GROVETB6612FNGMotorNoStandbyAction", automation.Action
) )
GROVETB6612FNGMotorChangeAddressAction = grove_tb6612fng_ns.class_(
"GROVETB6612FNGMotorChangeAddressAction", automation.Action
)
DIRECTION_TYPE = { DIRECTION_TYPE = {
"FORWARD": 1, "FORWARD": 1,
@ -150,3 +156,22 @@ async def grove_tb6612fng_no_standby_to_code(config, action_id, template_arg, ar
await cg.register_parented(var, config[CONF_ID]) await cg.register_parented(var, config[CONF_ID])
return var return var
@automation.register_action(
"grove_tb6612fng.change_address",
GROVETB6612FNGMotorChangeAddressAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
cv.Required(CONF_ADDRESS): cv.i2c_address,
}
),
)
async def grove_tb6612fng_change_address_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
template_channel = await cg.templatable(config[CONF_ADDRESS], args, int)
cg.add(var.set_address(template_channel))
return var

View file

@ -84,8 +84,7 @@ class GroveMotorDriveTB6612FNG : public Component, public i2c::I2CDevice {
*************************************************************/ *************************************************************/
void set_i2c_addr(uint8_t addr); void set_i2c_addr(uint8_t addr);
/************************************************************* /***********************************change_address
Description
Drive a motor. Drive a motor.
Parameter Parameter
chl: MOTOR_CHA or MOTOR_CHB chl: MOTOR_CHA or MOTOR_CHB
@ -204,5 +203,13 @@ class GROVETB6612FNGMotorNoStandbyAction : public Action<Ts...>, public Parented
void play(Ts... x) override { this->parent_->not_standby(); } void play(Ts... x) override { this->parent_->not_standby(); }
}; };
template<typename... Ts>
class GROVETB6612FNGMotorChangeAddressAction : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
public:
TEMPLATABLE_VALUE(uint8_t, address)
void play(Ts... x) override { this->parent_->set_i2c_addr(this->address_.value(x...)); }
};
} // namespace grove_tb6612fng } // namespace grove_tb6612fng
} // namespace esphome } // namespace esphome

View file

@ -37,6 +37,8 @@ void I2SAudioMicrophone::setup() {
void I2SAudioMicrophone::start() { void I2SAudioMicrophone::start() {
if (this->is_failed()) if (this->is_failed())
return; return;
if (this->state_ == microphone::STATE_RUNNING)
return; // Already running
this->state_ = microphone::STATE_STARTING; this->state_ = microphone::STATE_STARTING;
} }
void I2SAudioMicrophone::start_() { void I2SAudioMicrophone::start_() {

View file

@ -158,8 +158,13 @@ void I2SAudioSpeaker::watch_() {
if (xQueueReceive(this->event_queue_, &event, 0) == pdTRUE) { if (xQueueReceive(this->event_queue_, &event, 0) == pdTRUE) {
switch (event.type) { switch (event.type) {
case TaskEventType::STARTING: case TaskEventType::STARTING:
ESP_LOGD(TAG, "Starting I2S Audio Speaker");
break;
case TaskEventType::STARTED: case TaskEventType::STARTED:
ESP_LOGD(TAG, "Started I2S Audio Speaker");
break;
case TaskEventType::STOPPING: case TaskEventType::STOPPING:
ESP_LOGD(TAG, "Stopping I2S Audio Speaker");
break; break;
case TaskEventType::PLAYING: case TaskEventType::PLAYING:
this->status_clear_warning(); this->status_clear_warning();
@ -170,6 +175,7 @@ void I2SAudioSpeaker::watch_() {
this->player_task_handle_ = nullptr; this->player_task_handle_ = nullptr;
this->parent_->unlock(); this->parent_->unlock();
xQueueReset(this->buffer_queue_); xQueueReset(this->buffer_queue_);
ESP_LOGD(TAG, "Stopped I2S Audio Speaker");
break; break;
case TaskEventType::WARNING: case TaskEventType::WARNING:
ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err)); ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err));

View file

@ -87,7 +87,7 @@ struct IPAddress {
bool is_ip6() { return IP_IS_V6(&ip_addr_); } bool is_ip6() { return IP_IS_V6(&ip_addr_); }
std::string str() const { return ipaddr_ntoa(&ip_addr_); } std::string str() const { return ipaddr_ntoa(&ip_addr_); }
bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); } bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
bool operator!=(const IPAddress &other) const { return !(&ip_addr_ == &other.ip_addr_); } bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
IPAddress &operator+=(uint8_t increase) { IPAddress &operator+=(uint8_t increase) {
if (IP_IS_V4(&ip_addr_)) { if (IP_IS_V4(&ip_addr_)) {
#if LWIP_IPV6 #if LWIP_IPV6

View file

@ -1,5 +1,3 @@
#ifdef USE_ARDUINO
#include "prometheus_handler.h" #include "prometheus_handler.h"
#include "esphome/core/application.h" #include "esphome/core/application.h"
@ -89,7 +87,7 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor
stream->print(obj->get_unit_of_measurement().c_str()); stream->print(obj->get_unit_of_measurement().c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(value_accuracy_to_string(obj->state, obj->get_accuracy_decimals()).c_str()); stream->print(value_accuracy_to_string(obj->state, obj->get_accuracy_decimals()).c_str());
stream->print('\n'); stream->print(F("\n"));
} else { } else {
// Invalid state // Invalid state
stream->print(F("esphome_sensor_failed{id=\"")); stream->print(F("esphome_sensor_failed{id=\""));
@ -124,7 +122,7 @@ void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_s
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->state); stream->print(obj->state);
stream->print('\n'); stream->print(F("\n"));
} else { } else {
// Invalid state // Invalid state
stream->print(F("esphome_binary_sensor_failed{id=\"")); stream->print(F("esphome_binary_sensor_failed{id=\""));
@ -158,7 +156,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->state); stream->print(obj->state);
stream->print('\n'); stream->print(F("\n"));
// Speed if available // Speed if available
if (obj->get_traits().supports_speed()) { if (obj->get_traits().supports_speed()) {
stream->print(F("esphome_fan_speed{id=\"")); stream->print(F("esphome_fan_speed{id=\""));
@ -167,7 +165,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->speed); stream->print(obj->speed);
stream->print('\n'); stream->print(F("\n"));
} }
// Oscillation if available // Oscillation if available
if (obj->get_traits().supports_oscillation()) { if (obj->get_traits().supports_oscillation()) {
@ -177,7 +175,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->oscillating); stream->print(obj->oscillating);
stream->print('\n'); stream->print(F("\n"));
} }
} }
#endif #endif
@ -281,7 +279,7 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->position); stream->print(obj->position);
stream->print('\n'); stream->print(F("\n"));
if (obj->get_traits().get_supports_tilt()) { if (obj->get_traits().get_supports_tilt()) {
stream->print(F("esphome_cover_tilt{id=\"")); stream->print(F("esphome_cover_tilt{id=\""));
stream->print(relabel_id_(obj).c_str()); stream->print(relabel_id_(obj).c_str());
@ -289,7 +287,7 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->tilt); stream->print(obj->tilt);
stream->print('\n'); stream->print(F("\n"));
} }
} else { } else {
// Invalid state // Invalid state
@ -322,7 +320,7 @@ void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->state); stream->print(obj->state);
stream->print('\n'); stream->print(F("\n"));
} }
#endif #endif
@ -346,11 +344,9 @@ void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj)
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->state); stream->print(obj->state);
stream->print('\n'); stream->print(F("\n"));
} }
#endif #endif
} // namespace prometheus } // namespace prometheus
} // namespace esphome } // namespace esphome
#endif // USE_ARDUINO

View file

@ -1,14 +1,12 @@
#pragma once #pragma once
#ifdef USE_ARDUINO
#include <map> #include <map>
#include <utility> #include <utility>
#include "esphome/core/entity_base.h"
#include "esphome/components/web_server_base/web_server_base.h" #include "esphome/components/web_server_base/web_server_base.h"
#include "esphome/core/controller.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/controller.h"
#include "esphome/core/entity_base.h"
namespace esphome { namespace esphome {
namespace prometheus { namespace prometheus {
@ -119,5 +117,3 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
} // namespace prometheus } // namespace prometheus
} // namespace esphome } // namespace esphome
#endif // USE_ARDUINO

View file

@ -281,11 +281,15 @@ void VoiceAssistant::loop() {
memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written);
this->speaker_buffer_size_ -= written; this->speaker_buffer_size_ -= written;
this->speaker_buffer_index_ -= written; this->speaker_buffer_index_ -= written;
this->set_timeout("speaker-timeout", 1000, [this]() { this->speaker_->stop(); }); this->set_timeout("speaker-timeout", 2000, [this]() { this->speaker_->stop(); });
} else { } else {
ESP_LOGW(TAG, "Speaker buffer full."); ESP_LOGW(TAG, "Speaker buffer full.");
} }
} }
if (this->wait_for_stream_end_) {
this->cancel_timeout("playing");
break; // We dont want to timeout here as the STREAM_END event will take care of that.
}
playing = this->speaker_->is_running(); playing = this->speaker_->is_running();
} }
#endif #endif
@ -295,28 +299,77 @@ void VoiceAssistant::loop() {
} }
#endif #endif
if (playing) { if (playing) {
this->set_timeout("playing", 100, [this]() { this->set_timeout("playing", 2000, [this]() {
this->cancel_timeout("speaker-timeout"); this->cancel_timeout("speaker-timeout");
this->set_state_(State::IDLE, State::IDLE); this->set_state_(State::IDLE, State::IDLE);
}); });
} }
break; break;
} }
case State::RESPONSE_FINISHED: {
#ifdef USE_SPEAKER
if (this->speaker_ != nullptr) {
this->speaker_->stop();
this->cancel_timeout("speaker-timeout");
this->cancel_timeout("playing");
this->speaker_buffer_size_ = 0;
this->speaker_buffer_index_ = 0;
memset(this->speaker_buffer_, 0, SPEAKER_BUFFER_SIZE);
}
#endif
this->wait_for_stream_end_ = false;
this->set_state_(State::IDLE, State::IDLE);
break;
}
default: default:
break; break;
} }
} }
static const LogString *voice_assistant_state_to_string(State state) {
switch (state) {
case State::IDLE:
return LOG_STR("IDLE");
case State::START_MICROPHONE:
return LOG_STR("START_MICROPHONE");
case State::STARTING_MICROPHONE:
return LOG_STR("STARTING_MICROPHONE");
case State::WAIT_FOR_VAD:
return LOG_STR("WAIT_FOR_VAD");
case State::WAITING_FOR_VAD:
return LOG_STR("WAITING_FOR_VAD");
case State::START_PIPELINE:
return LOG_STR("START_PIPELINE");
case State::STARTING_PIPELINE:
return LOG_STR("STARTING_PIPELINE");
case State::STREAMING_MICROPHONE:
return LOG_STR("STREAMING_MICROPHONE");
case State::STOP_MICROPHONE:
return LOG_STR("STOP_MICROPHONE");
case State::STOPPING_MICROPHONE:
return LOG_STR("STOPPING_MICROPHONE");
case State::AWAITING_RESPONSE:
return LOG_STR("AWAITING_RESPONSE");
case State::STREAMING_RESPONSE:
return LOG_STR("STREAMING_RESPONSE");
case State::RESPONSE_FINISHED:
return LOG_STR("RESPONSE_FINISHED");
default:
return LOG_STR("UNKNOWN");
}
};
void VoiceAssistant::set_state_(State state) { void VoiceAssistant::set_state_(State state) {
State old_state = this->state_; State old_state = this->state_;
this->state_ = state; this->state_ = state;
ESP_LOGD(TAG, "State changed from %d to %d", static_cast<uint8_t>(old_state), static_cast<uint8_t>(state)); ESP_LOGD(TAG, "State changed from %s to %s", LOG_STR_ARG(voice_assistant_state_to_string(old_state)),
LOG_STR_ARG(voice_assistant_state_to_string(state)));
} }
void VoiceAssistant::set_state_(State state, State desired_state) { void VoiceAssistant::set_state_(State state, State desired_state) {
this->set_state_(state); this->set_state_(state);
this->desired_state_ = desired_state; this->desired_state_ = desired_state;
ESP_LOGD(TAG, "Desired state set to %d", static_cast<uint8_t>(desired_state)); ESP_LOGD(TAG, "Desired state set to %s", LOG_STR_ARG(voice_assistant_state_to_string(desired_state)));
} }
void VoiceAssistant::failed_to_start() { void VoiceAssistant::failed_to_start() {
@ -400,6 +453,7 @@ void VoiceAssistant::request_stop() {
break; break;
case State::AWAITING_RESPONSE: case State::AWAITING_RESPONSE:
case State::STREAMING_RESPONSE: case State::STREAMING_RESPONSE:
case State::RESPONSE_FINISHED:
break; // Let the incoming audio stream finish then it will go to idle. break; // Let the incoming audio stream finish then it will go to idle.
} }
} }
@ -531,6 +585,14 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
this->error_trigger_->trigger(code, message); this->error_trigger_->trigger(code, message);
break; break;
} }
case api::enums::VOICE_ASSISTANT_TTS_STREAM_START: {
this->wait_for_stream_end_ = true;
break;
}
case api::enums::VOICE_ASSISTANT_TTS_STREAM_END: {
this->set_state_(State::RESPONSE_FINISHED, State::IDLE);
break;
}
default: default:
ESP_LOGD(TAG, "Unhandled event type: %d", msg.event_type); ESP_LOGD(TAG, "Unhandled event type: %d", msg.event_type);
break; break;

View file

@ -46,6 +46,7 @@ enum class State {
STOPPING_MICROPHONE, STOPPING_MICROPHONE,
AWAITING_RESPONSE, AWAITING_RESPONSE,
STREAMING_RESPONSE, STREAMING_RESPONSE,
RESPONSE_FINISHED,
}; };
class VoiceAssistant : public Component { class VoiceAssistant : public Component {
@ -132,10 +133,10 @@ class VoiceAssistant : public Component {
uint8_t *speaker_buffer_; uint8_t *speaker_buffer_;
size_t speaker_buffer_index_{0}; size_t speaker_buffer_index_{0};
size_t speaker_buffer_size_{0}; size_t speaker_buffer_size_{0};
bool wait_for_stream_end_{false};
#endif #endif
#ifdef USE_MEDIA_PLAYER #ifdef USE_MEDIA_PLAYER
media_player::MediaPlayer *media_player_{nullptr}; media_player::MediaPlayer *media_player_{nullptr};
bool playing_tts_{false};
#endif #endif
bool local_output_{false}; bool local_output_{false};

View file

@ -1,6 +1,6 @@
"""Constants used by esphome.""" """Constants used by esphome."""
__version__ = "2023.10.0b2" __version__ = "2023.10.0b3"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
VALID_SUBSTITUTIONS_CHARACTERS = ( VALID_SUBSTITUTIONS_CHARACTERS = (