mirror of
https://github.com/esphome/esphome.git
synced 2024-11-26 08:55:22 +01:00
[voice_assistant] Timers (#6821)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
parent
5ae32e81c3
commit
854d3f2e4a
10 changed files with 321 additions and 0 deletions
|
@ -1517,6 +1517,25 @@ message VoiceAssistantAudio {
|
||||||
bool end = 2;
|
bool end = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum VoiceAssistantTimerEvent {
|
||||||
|
VOICE_ASSISTANT_TIMER_STARTED = 0;
|
||||||
|
VOICE_ASSISTANT_TIMER_UPDATED = 1;
|
||||||
|
VOICE_ASSISTANT_TIMER_CANCELLED = 2;
|
||||||
|
VOICE_ASSISTANT_TIMER_FINISHED = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VoiceAssistantTimerEventResponse {
|
||||||
|
option (id) = 115;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||||
|
|
||||||
|
VoiceAssistantTimerEvent event_type = 1;
|
||||||
|
string timer_id = 2;
|
||||||
|
string name = 3;
|
||||||
|
uint32 total_seconds = 4;
|
||||||
|
uint32 seconds_left = 5;
|
||||||
|
bool is_active = 6;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== ALARM CONTROL PANEL ====================
|
// ==================== ALARM CONTROL PANEL ====================
|
||||||
enum AlarmControlPanelState {
|
enum AlarmControlPanelState {
|
||||||
|
|
|
@ -1193,6 +1193,15 @@ void APIConnection::on_voice_assistant_audio(const VoiceAssistantAudio &msg) {
|
||||||
voice_assistant::global_voice_assistant->on_audio(msg);
|
voice_assistant::global_voice_assistant->on_audio(msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
void APIConnection::on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) {
|
||||||
|
if (voice_assistant::global_voice_assistant != nullptr) {
|
||||||
|
if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice_assistant::global_voice_assistant->on_timer_event(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,7 @@ class APIConnection : public APIServerConnection {
|
||||||
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
||||||
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
||||||
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
|
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
|
||||||
|
void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
|
|
@ -475,6 +475,22 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
template<> const char *proto_enum_to_string<enums::VoiceAssistantTimerEvent>(enums::VoiceAssistantTimerEvent value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::VOICE_ASSISTANT_TIMER_STARTED:
|
||||||
|
return "VOICE_ASSISTANT_TIMER_STARTED";
|
||||||
|
case enums::VOICE_ASSISTANT_TIMER_UPDATED:
|
||||||
|
return "VOICE_ASSISTANT_TIMER_UPDATED";
|
||||||
|
case enums::VOICE_ASSISTANT_TIMER_CANCELLED:
|
||||||
|
return "VOICE_ASSISTANT_TIMER_CANCELLED";
|
||||||
|
case enums::VOICE_ASSISTANT_TIMER_FINISHED:
|
||||||
|
return "VOICE_ASSISTANT_TIMER_FINISHED";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
template<> const char *proto_enum_to_string<enums::AlarmControlPanelState>(enums::AlarmControlPanelState value) {
|
template<> const char *proto_enum_to_string<enums::AlarmControlPanelState>(enums::AlarmControlPanelState value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case enums::ALARM_STATE_DISARMED:
|
case enums::ALARM_STATE_DISARMED:
|
||||||
|
@ -6857,6 +6873,82 @@ void VoiceAssistantAudio::dump_to(std::string &out) const {
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
bool VoiceAssistantTimerEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->event_type = value.as_enum<enums::VoiceAssistantTimerEvent>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->total_seconds = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->seconds_left = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 6: {
|
||||||
|
this->is_active = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool VoiceAssistantTimerEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->timer_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->name = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void VoiceAssistantTimerEventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_enum<enums::VoiceAssistantTimerEvent>(1, this->event_type);
|
||||||
|
buffer.encode_string(2, this->timer_id);
|
||||||
|
buffer.encode_string(3, this->name);
|
||||||
|
buffer.encode_uint32(4, this->total_seconds);
|
||||||
|
buffer.encode_uint32(5, this->seconds_left);
|
||||||
|
buffer.encode_bool(6, this->is_active);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void VoiceAssistantTimerEventResponse::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("VoiceAssistantTimerEventResponse {\n");
|
||||||
|
out.append(" event_type: ");
|
||||||
|
out.append(proto_enum_to_string<enums::VoiceAssistantTimerEvent>(this->event_type));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" timer_id: ");
|
||||||
|
out.append("'").append(this->timer_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" name: ");
|
||||||
|
out.append("'").append(this->name).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" total_seconds: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->total_seconds);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" seconds_left: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->seconds_left);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" is_active: ");
|
||||||
|
out.append(YESNO(this->is_active));
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 6: {
|
case 6: {
|
||||||
|
|
|
@ -191,6 +191,12 @@ enum VoiceAssistantEvent : uint32_t {
|
||||||
VOICE_ASSISTANT_TTS_STREAM_START = 98,
|
VOICE_ASSISTANT_TTS_STREAM_START = 98,
|
||||||
VOICE_ASSISTANT_TTS_STREAM_END = 99,
|
VOICE_ASSISTANT_TTS_STREAM_END = 99,
|
||||||
};
|
};
|
||||||
|
enum VoiceAssistantTimerEvent : uint32_t {
|
||||||
|
VOICE_ASSISTANT_TIMER_STARTED = 0,
|
||||||
|
VOICE_ASSISTANT_TIMER_UPDATED = 1,
|
||||||
|
VOICE_ASSISTANT_TIMER_CANCELLED = 2,
|
||||||
|
VOICE_ASSISTANT_TIMER_FINISHED = 3,
|
||||||
|
};
|
||||||
enum AlarmControlPanelState : uint32_t {
|
enum AlarmControlPanelState : uint32_t {
|
||||||
ALARM_STATE_DISARMED = 0,
|
ALARM_STATE_DISARMED = 0,
|
||||||
ALARM_STATE_ARMED_HOME = 1,
|
ALARM_STATE_ARMED_HOME = 1,
|
||||||
|
@ -1775,6 +1781,23 @@ class VoiceAssistantAudio : public ProtoMessage {
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
|
class VoiceAssistantTimerEventResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
enums::VoiceAssistantTimerEvent event_type{};
|
||||||
|
std::string timer_id{};
|
||||||
|
std::string name{};
|
||||||
|
uint32_t total_seconds{0};
|
||||||
|
uint32_t seconds_left{0};
|
||||||
|
bool is_active{false};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{};
|
std::string object_id{};
|
||||||
|
|
|
@ -484,6 +484,8 @@ bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAud
|
||||||
return this->send_message_<VoiceAssistantAudio>(msg, 106);
|
return this->send_message_<VoiceAssistantAudio>(msg, 106);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
|
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
|
||||||
const ListEntitiesAlarmControlPanelResponse &msg) {
|
const ListEntitiesAlarmControlPanelResponse &msg) {
|
||||||
|
@ -1093,6 +1095,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||||
ESP_LOGVV(TAG, "on_date_time_command_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_date_time_command_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
this->on_date_time_command_request(msg);
|
this->on_date_time_command_request(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 115: {
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
VoiceAssistantTimerEventResponse msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_voice_assistant_timer_event_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_voice_assistant_timer_event_response(msg);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,6 +244,9 @@ class APIServerConnectionBase : public ProtoService {
|
||||||
bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
|
bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
|
||||||
virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
|
virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
virtual void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &value){};
|
||||||
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
|
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -44,6 +44,12 @@ CONF_VOLUME_MULTIPLIER = "volume_multiplier"
|
||||||
|
|
||||||
CONF_WAKE_WORD = "wake_word"
|
CONF_WAKE_WORD = "wake_word"
|
||||||
|
|
||||||
|
CONF_ON_TIMER_STARTED = "on_timer_started"
|
||||||
|
CONF_ON_TIMER_UPDATED = "on_timer_updated"
|
||||||
|
CONF_ON_TIMER_CANCELLED = "on_timer_cancelled"
|
||||||
|
CONF_ON_TIMER_FINISHED = "on_timer_finished"
|
||||||
|
CONF_ON_TIMER_TICK = "on_timer_tick"
|
||||||
|
|
||||||
|
|
||||||
voice_assistant_ns = cg.esphome_ns.namespace("voice_assistant")
|
voice_assistant_ns = cg.esphome_ns.namespace("voice_assistant")
|
||||||
VoiceAssistant = voice_assistant_ns.class_("VoiceAssistant", cg.Component)
|
VoiceAssistant = voice_assistant_ns.class_("VoiceAssistant", cg.Component)
|
||||||
|
@ -64,6 +70,8 @@ ConnectedCondition = voice_assistant_ns.class_(
|
||||||
"ConnectedCondition", automation.Condition, cg.Parented.template(VoiceAssistant)
|
"ConnectedCondition", automation.Condition, cg.Parented.template(VoiceAssistant)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Timer = voice_assistant_ns.struct("Timer")
|
||||||
|
|
||||||
|
|
||||||
def tts_stream_validate(config):
|
def tts_stream_validate(config):
|
||||||
if CONF_SPEAKER not in config and (
|
if CONF_SPEAKER not in config and (
|
||||||
|
@ -131,6 +139,21 @@ CONFIG_SCHEMA = cv.All(
|
||||||
single=True
|
single=True
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_IDLE): automation.validate_automation(single=True),
|
cv.Optional(CONF_ON_IDLE): automation.validate_automation(single=True),
|
||||||
|
cv.Optional(CONF_ON_TIMER_STARTED): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_TIMER_UPDATED): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_TIMER_CANCELLED): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_TIMER_FINISHED): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_TIMER_TICK): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
tts_stream_validate,
|
tts_stream_validate,
|
||||||
|
@ -270,6 +293,49 @@ async def to_code(config):
|
||||||
config[CONF_ON_IDLE],
|
config[CONF_ON_IDLE],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
has_timers = False
|
||||||
|
if on_timer_started := config.get(CONF_ON_TIMER_STARTED):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_timer_started_trigger(),
|
||||||
|
[(Timer, "timer")],
|
||||||
|
on_timer_started,
|
||||||
|
)
|
||||||
|
has_timers = True
|
||||||
|
|
||||||
|
if on_timer_updated := config.get(CONF_ON_TIMER_UPDATED):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_timer_updated_trigger(),
|
||||||
|
[(Timer, "timer")],
|
||||||
|
on_timer_updated,
|
||||||
|
)
|
||||||
|
has_timers = True
|
||||||
|
|
||||||
|
if on_timer_cancelled := config.get(CONF_ON_TIMER_CANCELLED):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_timer_cancelled_trigger(),
|
||||||
|
[(Timer, "timer")],
|
||||||
|
on_timer_cancelled,
|
||||||
|
)
|
||||||
|
has_timers = True
|
||||||
|
|
||||||
|
if on_timer_finished := config.get(CONF_ON_TIMER_FINISHED):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_timer_finished_trigger(),
|
||||||
|
[(Timer, "timer")],
|
||||||
|
on_timer_finished,
|
||||||
|
)
|
||||||
|
has_timers = True
|
||||||
|
|
||||||
|
if on_timer_tick := config.get(CONF_ON_TIMER_TICK):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_timer_tick_trigger(),
|
||||||
|
[(cg.std_vector.template(Timer), "timers")],
|
||||||
|
on_timer_tick,
|
||||||
|
)
|
||||||
|
has_timers = True
|
||||||
|
|
||||||
|
cg.add(var.set_has_timers(has_timers))
|
||||||
|
|
||||||
cg.add_define("USE_VOICE_ASSISTANT")
|
cg.add_define("USE_VOICE_ASSISTANT")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -798,12 +798,65 @@ void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) {
|
||||||
this->speaker_buffer_index_ += msg.data.length();
|
this->speaker_buffer_index_ += msg.data.length();
|
||||||
this->speaker_buffer_size_ += msg.data.length();
|
this->speaker_buffer_size_ += msg.data.length();
|
||||||
this->speaker_bytes_received_ += msg.data.length();
|
this->speaker_bytes_received_ += msg.data.length();
|
||||||
|
ESP_LOGD(TAG, "Received audio: %d bytes from API", msg.data.length());
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "Cannot receive audio, buffer is full");
|
ESP_LOGE(TAG, "Cannot receive audio, buffer is full");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VoiceAssistant::on_timer_event(const api::VoiceAssistantTimerEventResponse &msg) {
|
||||||
|
Timer timer = {
|
||||||
|
.id = msg.timer_id,
|
||||||
|
.name = msg.name,
|
||||||
|
.total_seconds = msg.total_seconds,
|
||||||
|
.seconds_left = msg.seconds_left,
|
||||||
|
.is_active = msg.is_active,
|
||||||
|
};
|
||||||
|
this->timers_[timer.id] = timer;
|
||||||
|
ESP_LOGD(TAG, "Timer Event");
|
||||||
|
ESP_LOGD(TAG, " Type: %d", msg.event_type);
|
||||||
|
ESP_LOGD(TAG, " %s", timer.to_string().c_str());
|
||||||
|
|
||||||
|
switch (msg.event_type) {
|
||||||
|
case api::enums::VOICE_ASSISTANT_TIMER_STARTED:
|
||||||
|
this->timer_started_trigger_->trigger(timer);
|
||||||
|
break;
|
||||||
|
case api::enums::VOICE_ASSISTANT_TIMER_UPDATED:
|
||||||
|
this->timer_updated_trigger_->trigger(timer);
|
||||||
|
break;
|
||||||
|
case api::enums::VOICE_ASSISTANT_TIMER_CANCELLED:
|
||||||
|
this->timer_cancelled_trigger_->trigger(timer);
|
||||||
|
this->timers_.erase(timer.id);
|
||||||
|
break;
|
||||||
|
case api::enums::VOICE_ASSISTANT_TIMER_FINISHED:
|
||||||
|
this->timer_finished_trigger_->trigger(timer);
|
||||||
|
this->timers_.erase(timer.id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->timers_.empty()) {
|
||||||
|
this->cancel_interval("timer-event");
|
||||||
|
this->timer_tick_running_ = false;
|
||||||
|
} else if (!this->timer_tick_running_) {
|
||||||
|
this->set_interval("timer-event", 1000, [this]() { this->timer_tick_(); });
|
||||||
|
this->timer_tick_running_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoiceAssistant::timer_tick_() {
|
||||||
|
std::vector<Timer> res;
|
||||||
|
res.reserve(this->timers_.size());
|
||||||
|
for (auto &pair : this->timers_) {
|
||||||
|
auto &timer = pair.second;
|
||||||
|
if (timer.is_active && timer.seconds_left > 0) {
|
||||||
|
timer.seconds_left--;
|
||||||
|
}
|
||||||
|
res.push_back(timer);
|
||||||
|
}
|
||||||
|
this->timer_tick_trigger_->trigger(res);
|
||||||
|
}
|
||||||
|
|
||||||
VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
} // namespace voice_assistant
|
} // namespace voice_assistant
|
||||||
|
|
|
@ -24,6 +24,9 @@
|
||||||
#include <esp_vad.h>
|
#include <esp_vad.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace voice_assistant {
|
namespace voice_assistant {
|
||||||
|
|
||||||
|
@ -36,6 +39,7 @@ enum VoiceAssistantFeature : uint32_t {
|
||||||
FEATURE_VOICE_ASSISTANT = 1 << 0,
|
FEATURE_VOICE_ASSISTANT = 1 << 0,
|
||||||
FEATURE_SPEAKER = 1 << 1,
|
FEATURE_SPEAKER = 1 << 1,
|
||||||
FEATURE_API_AUDIO = 1 << 2,
|
FEATURE_API_AUDIO = 1 << 2,
|
||||||
|
FEATURE_TIMERS = 1 << 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class State {
|
enum class State {
|
||||||
|
@ -59,6 +63,20 @@ enum AudioMode : uint8_t {
|
||||||
AUDIO_MODE_API,
|
AUDIO_MODE_API,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Timer {
|
||||||
|
std::string id;
|
||||||
|
std::string name;
|
||||||
|
uint32_t total_seconds;
|
||||||
|
uint32_t seconds_left;
|
||||||
|
bool is_active;
|
||||||
|
|
||||||
|
std::string to_string() const {
|
||||||
|
return str_sprintf("Timer(id=%s, name=%s, total_seconds=%" PRIu32 ", seconds_left=%" PRIu32 ", is_active=%s)",
|
||||||
|
this->id.c_str(), this->name.c_str(), this->total_seconds, this->seconds_left,
|
||||||
|
YESNO(this->is_active));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class VoiceAssistant : public Component {
|
class VoiceAssistant : public Component {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
@ -100,6 +118,11 @@ class VoiceAssistant : public Component {
|
||||||
flags |= VoiceAssistantFeature::FEATURE_SPEAKER;
|
flags |= VoiceAssistantFeature::FEATURE_SPEAKER;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (this->has_timers_) {
|
||||||
|
flags |= VoiceAssistantFeature::FEATURE_TIMERS;
|
||||||
|
}
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +131,7 @@ class VoiceAssistant : public Component {
|
||||||
|
|
||||||
void on_event(const api::VoiceAssistantEventResponse &msg);
|
void on_event(const api::VoiceAssistantEventResponse &msg);
|
||||||
void on_audio(const api::VoiceAssistantAudio &msg);
|
void on_audio(const api::VoiceAssistantAudio &msg);
|
||||||
|
void on_timer_event(const api::VoiceAssistantTimerEventResponse &msg);
|
||||||
|
|
||||||
bool is_running() const { return this->state_ != State::IDLE; }
|
bool is_running() const { return this->state_ != State::IDLE; }
|
||||||
void set_continuous(bool continuous) { this->continuous_ = continuous; }
|
void set_continuous(bool continuous) { this->continuous_ = continuous; }
|
||||||
|
@ -150,6 +174,14 @@ class VoiceAssistant : public Component {
|
||||||
|
|
||||||
void set_wake_word(const std::string &wake_word) { this->wake_word_ = wake_word; }
|
void set_wake_word(const std::string &wake_word) { this->wake_word_ = wake_word; }
|
||||||
|
|
||||||
|
Trigger<Timer> *get_timer_started_trigger() const { return this->timer_started_trigger_; }
|
||||||
|
Trigger<Timer> *get_timer_updated_trigger() const { return this->timer_updated_trigger_; }
|
||||||
|
Trigger<Timer> *get_timer_cancelled_trigger() const { return this->timer_cancelled_trigger_; }
|
||||||
|
Trigger<Timer> *get_timer_finished_trigger() const { return this->timer_finished_trigger_; }
|
||||||
|
Trigger<std::vector<Timer>> *get_timer_tick_trigger() const { return this->timer_tick_trigger_; }
|
||||||
|
void set_has_timers(bool has_timers) { this->has_timers_ = has_timers; }
|
||||||
|
const std::unordered_map<std::string, Timer> &get_timers() const { return this->timers_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool allocate_buffers_();
|
bool allocate_buffers_();
|
||||||
void clear_buffers_();
|
void clear_buffers_();
|
||||||
|
@ -186,6 +218,16 @@ class VoiceAssistant : public Component {
|
||||||
|
|
||||||
api::APIConnection *api_client_{nullptr};
|
api::APIConnection *api_client_{nullptr};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, Timer> timers_;
|
||||||
|
void timer_tick_();
|
||||||
|
Trigger<Timer> *timer_started_trigger_ = new Trigger<Timer>();
|
||||||
|
Trigger<Timer> *timer_finished_trigger_ = new Trigger<Timer>();
|
||||||
|
Trigger<Timer> *timer_updated_trigger_ = new Trigger<Timer>();
|
||||||
|
Trigger<Timer> *timer_cancelled_trigger_ = new Trigger<Timer>();
|
||||||
|
Trigger<std::vector<Timer>> *timer_tick_trigger_ = new Trigger<std::vector<Timer>>();
|
||||||
|
bool has_timers_{false};
|
||||||
|
bool timer_tick_running_{false};
|
||||||
|
|
||||||
microphone::Microphone *mic_{nullptr};
|
microphone::Microphone *mic_{nullptr};
|
||||||
#ifdef USE_SPEAKER
|
#ifdef USE_SPEAKER
|
||||||
void write_speaker_();
|
void write_speaker_();
|
||||||
|
|
Loading…
Reference in a new issue