diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 04db649aef..5557c56240 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1449,6 +1449,7 @@ message VoiceAssistantRequest { string conversation_id = 2; uint32 flags = 3; VoiceAssistantAudioSettings audio_settings = 4; + string wake_word_phrase = 5; } message VoiceAssistantResponse { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index f81bf04e99..dccebf0d5d 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -6594,6 +6594,10 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite this->audio_settings = value.as_message(); return true; } + case 5: { + this->wake_word_phrase = value.as_string(); + return true; + } default: return false; } @@ -6603,6 +6607,7 @@ void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(2, this->conversation_id); buffer.encode_uint32(3, this->flags); buffer.encode_message(4, this->audio_settings); + buffer.encode_string(5, this->wake_word_phrase); } #ifdef HAS_PROTO_MESSAGE_DUMP void VoiceAssistantRequest::dump_to(std::string &out) const { @@ -6624,6 +6629,10 @@ void VoiceAssistantRequest::dump_to(std::string &out) const { out.append(" audio_settings: "); this->audio_settings.dump_to(out); out.append("\n"); + + out.append(" wake_word_phrase: "); + out.append("'").append(this->wake_word_phrase).append("'"); + out.append("\n"); out.append("}"); } #endif diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 02fc7b88f8..2854a2e9c1 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1701,6 +1701,7 @@ class VoiceAssistantRequest : public ProtoMessage { std::string conversation_id{}; uint32_t flags{0}; VoiceAssistantAudioSettings audio_settings{}; + std::string wake_word_phrase{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index f482ba26c3..cdd4220e50 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -118,7 +118,7 @@ void CSE7766Component::parse_data_() { uint32_t power_coeff = this->get_24_bit_uint_(14); uint32_t power_cycle = this->get_24_bit_uint_(17); uint8_t adj = this->raw_data_[20]; - uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22]; + uint16_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22]; bool have_power = adj & 0x10; bool have_current = adj & 0x20; @@ -132,8 +132,19 @@ void CSE7766Component::parse_data_() { } } + float energy = 0.0; + if (this->energy_sensor_ != nullptr) { + if (this->cf_pulses_last_ == 0 && !this->energy_sensor_->has_state()) { + this->cf_pulses_last_ = cf_pulses; + } + uint16_t cf_diff = cf_pulses - this->cf_pulses_last_; + this->cf_pulses_total_ += cf_diff; + this->cf_pulses_last_ = cf_pulses; + energy = this->cf_pulses_total_ * float(power_coeff) / 1000000.0f / 3600.0f; + this->energy_sensor_->publish_state(energy); + } + float power = 0.0f; - float energy = 0.0f; if (power_cycle_exceeds_range) { // Datasheet: power cycle exceeding range means active power is 0 if (this->power_sensor_ != nullptr) { @@ -144,27 +155,6 @@ void CSE7766Component::parse_data_() { if (this->power_sensor_ != nullptr) { this->power_sensor_->publish_state(power); } - - // Add CF pulses to the total energy only if we have Power coefficient to multiply by - - if (this->cf_pulses_last_ == 0) { - this->cf_pulses_last_ = cf_pulses; - } - - uint32_t cf_diff; - if (cf_pulses < this->cf_pulses_last_) { - cf_diff = cf_pulses + (0x10000 - this->cf_pulses_last_); - } else { - cf_diff = cf_pulses - this->cf_pulses_last_; - } - this->cf_pulses_last_ = cf_pulses; - - energy = cf_diff * float(power_coeff) / 1000000.0f / 3600.0f; - this->energy_total_ += energy; - if (this->energy_sensor_ != nullptr) - this->energy_sensor_->publish_state(this->energy_total_); - } else if ((this->energy_sensor_ != nullptr) && !this->energy_sensor_->has_state()) { - this->energy_sensor_->publish_state(0); } float current = 0.0f; diff --git a/esphome/components/cse7766/cse7766.h b/esphome/components/cse7766/cse7766.h index 3ab8d609bd..4d9ba7be74 100644 --- a/esphome/components/cse7766/cse7766.h +++ b/esphome/components/cse7766/cse7766.h @@ -30,8 +30,8 @@ class CSE7766Component : public Component, public uart::UARTDevice { sensor::Sensor *current_sensor_{nullptr}; sensor::Sensor *power_sensor_{nullptr}; sensor::Sensor *energy_sensor_{nullptr}; - float energy_total_{0.0f}; - uint32_t cf_pulses_last_{0}; + uint32_t cf_pulses_total_{0}; + uint16_t cf_pulses_last_{0}; }; } // namespace cse7766 diff --git a/esphome/components/micro_wake_word/micro_wake_word.cpp b/esphome/components/micro_wake_word/micro_wake_word.cpp index f0b3d55a9d..7321e5b05b 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.cpp +++ b/esphome/components/micro_wake_word/micro_wake_word.cpp @@ -134,7 +134,7 @@ void MicroWakeWord::loop() { this->set_state_(State::IDLE); if (this->detected_) { this->detected_ = false; - this->wake_word_detected_trigger_->trigger(""); + this->wake_word_detected_trigger_->trigger(this->wake_word_); } } break; diff --git a/esphome/components/speed/fan/__init__.py b/esphome/components/speed/fan/__init__.py index 221e67d6c7..4527e1eed1 100644 --- a/esphome/components/speed/fan/__init__.py +++ b/esphome/components/speed/fan/__init__.py @@ -14,6 +14,8 @@ from esphome.const import ( from .. import speed_ns +AUTO_LOAD = ["output"] + SpeedFan = speed_ns.class_("SpeedFan", cg.Component, fan.Fan) CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( diff --git a/esphome/components/tmp102/tmp102.cpp b/esphome/components/tmp102/tmp102.cpp index f6bb9a05c0..be60a2d8d4 100644 --- a/esphome/components/tmp102/tmp102.cpp +++ b/esphome/components/tmp102/tmp102.cpp @@ -28,7 +28,7 @@ void TMP102Component::dump_config() { } void TMP102Component::update() { - uint16_t raw_temperature; + int16_t raw_temperature; if (this->write(&TMP102_REGISTER_TEMPERATURE, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; @@ -39,7 +39,6 @@ void TMP102Component::update() { return; } raw_temperature = i2c::i2ctohs(raw_temperature); - raw_temperature = raw_temperature >> 4; float temperature = raw_temperature * TMP102_CONVERSION_FACTOR; ESP_LOGD(TAG, "Got Temperature=%.1f°C", temperature); diff --git a/esphome/components/voice_assistant/__init__.py b/esphome/components/voice_assistant/__init__.py index b21a5b27da..17bdffd9da 100644 --- a/esphome/components/voice_assistant/__init__.py +++ b/esphome/components/voice_assistant/__init__.py @@ -42,6 +42,8 @@ CONF_AUTO_GAIN = "auto_gain" CONF_NOISE_SUPPRESSION_LEVEL = "noise_suppression_level" CONF_VOLUME_MULTIPLIER = "volume_multiplier" +CONF_WAKE_WORD = "wake_word" + voice_assistant_ns = cg.esphome_ns.namespace("voice_assistant") VoiceAssistant = voice_assistant_ns.class_("VoiceAssistant", cg.Component) @@ -285,6 +287,7 @@ VOICE_ASSISTANT_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(VoiceAssis VOICE_ASSISTANT_ACTION_SCHEMA.extend( { cv.Optional(CONF_SILENCE_DETECTION, default=True): cv.boolean, + cv.Optional(CONF_WAKE_WORD): cv.templatable(cv.string), } ), ) @@ -293,6 +296,9 @@ async def voice_assistant_listen_to_code(config, action_id, template_arg, args): await cg.register_parented(var, config[CONF_ID]) if CONF_SILENCE_DETECTION in config: cg.add(var.set_silence_detection(config[CONF_SILENCE_DETECTION])) + if wake_word := config.get(CONF_WAKE_WORD): + templ = await cg.templatable(wake_word, args, cg.std_string) + cg.add(var.set_wake_word(templ)) return var diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 260605c0b4..49b8fdc959 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -215,6 +215,8 @@ void VoiceAssistant::loop() { msg.conversation_id = this->conversation_id_; msg.flags = flags; msg.audio_settings = audio_settings; + msg.wake_word_phrase = this->wake_word_; + this->wake_word_ = ""; if (this->api_client_ == nullptr || !this->api_client_->send_voice_assistant_request(msg)) { ESP_LOGW(TAG, "Could not request start"); diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index f0ee793f53..14352bf3ae 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -124,6 +124,8 @@ class VoiceAssistant : public Component { void client_subscription(api::APIConnection *client, bool subscribe); api::APIConnection *get_api_connection() const { return this->api_client_; } + void set_wake_word(const std::string &wake_word) { this->wake_word_ = wake_word; } + protected: int read_microphone_(); void set_state_(State state); @@ -175,6 +177,8 @@ class VoiceAssistant : public Component { std::string conversation_id_{""}; + std::string wake_word_{""}; + HighFrequencyLoopRequester high_freq_; #ifdef USE_ESP_ADF @@ -200,8 +204,13 @@ class VoiceAssistant : public Component { }; template class StartAction : public Action, public Parented { + TEMPLATABLE_VALUE(std::string, wake_word); + public: - void play(Ts... x) override { this->parent_->request_start(false, this->silence_detection_); } + void play(Ts... x) override { + this->parent_->set_wake_word(this->wake_word_.value(x...)); + this->parent_->request_start(false, this->silence_detection_); + } void set_silence_detection(bool silence_detection) { this->silence_detection_ = silence_detection; } diff --git a/esphome/const.py b/esphome/const.py index 717401df27..a305199585 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.2.1" +__version__ = "2024.2.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 8bcc8efa0b..de3fe6e8ae 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -820,6 +820,7 @@ class EditRequestHandler(BaseHandler): None, self._read_file, filename, configuration ) if content is not None: + self.set_header("Content-Type", "application/yaml") self.write(content) def _read_file(self, filename: str, configuration: str) -> bytes | None: