esphome 2024.6.6__py3-none-any.whl → 2024.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- esphome/components/aht10/aht10.cpp +4 -2
- esphome/components/climate/climate.cpp +10 -6
- esphome/components/climate/climate_traits.h +3 -3
- esphome/components/cover/cover.h +2 -2
- esphome/components/esp32_camera/__init__.py +6 -3
- esphome/components/esp32_can/canbus.py +3 -0
- esphome/components/ethernet/ethernet_component.cpp +8 -3
- esphome/components/font/__init__.py +2 -28
- esphome/components/gree/climate.py +1 -0
- esphome/components/gree/gree.cpp +11 -3
- esphome/components/gree/gree.h +5 -1
- esphome/components/haier/binary_sensor/__init__.py +4 -4
- esphome/components/haier/button/__init__.py +1 -1
- esphome/components/haier/climate.py +43 -9
- esphome/components/haier/haier_base.cpp +4 -0
- esphome/components/haier/haier_base.h +11 -1
- esphome/components/haier/hon_climate.cpp +109 -55
- esphome/components/haier/hon_climate.h +7 -1
- esphome/components/haier/hon_packet.h +5 -0
- esphome/components/haier/sensor/__init__.py +5 -5
- esphome/components/haier/smartair2_climate.cpp +1 -0
- esphome/components/haier/text_sensor/__init__.py +4 -4
- esphome/components/heatpumpir/climate.py +12 -5
- esphome/components/heatpumpir/heatpumpir.cpp +11 -0
- esphome/components/heatpumpir/heatpumpir.h +11 -0
- esphome/components/http_request/http_request_arduino.cpp +7 -2
- esphome/components/http_request/update/http_request_update.cpp +6 -7
- esphome/components/http_request/update/http_request_update.h +0 -3
- esphome/components/i2s_audio/__init__.py +10 -0
- esphome/components/i2s_audio/microphone/__init__.py +7 -0
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +2 -3
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +3 -0
- esphome/components/image/__init__.py +2 -29
- esphome/components/improv_serial/improv_serial_component.cpp +9 -8
- esphome/components/ltr390/ltr390.cpp +44 -29
- esphome/components/ltr390/ltr390.h +9 -5
- esphome/components/ltr390/sensor.py +35 -5
- esphome/components/mdns/__init__.py +3 -3
- esphome/components/mdns/mdns_component.cpp +3 -1
- esphome/components/mdns/mdns_component.h +3 -1
- esphome/components/mdns/mdns_esp32.cpp +2 -1
- esphome/components/mdns/mdns_esp8266.cpp +2 -1
- esphome/components/mdns/mdns_host.cpp +2 -1
- esphome/components/mdns/mdns_libretiny.cpp +2 -1
- esphome/components/mdns/mdns_rp2040.cpp +2 -1
- esphome/components/micro_wake_word/__init__.py +205 -56
- esphome/components/micro_wake_word/micro_wake_word.cpp +225 -275
- esphome/components/micro_wake_word/micro_wake_word.h +77 -107
- esphome/components/micro_wake_word/preprocessor_settings.h +20 -0
- esphome/components/micro_wake_word/streaming_model.cpp +189 -0
- esphome/components/micro_wake_word/streaming_model.h +84 -0
- esphome/components/mitsubishi/mitsubishi.cpp +1 -0
- esphome/components/modbus_controller/text_sensor/__init__.py +2 -1
- esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp +4 -1
- esphome/components/modbus_controller/text_sensor/modbus_textsensor.h +1 -1
- esphome/components/number/__init__.py +2 -0
- esphome/components/ota/ota_backend_arduino_esp32.cpp +22 -7
- esphome/components/ota/ota_backend_arduino_esp8266.cpp +23 -8
- esphome/components/ota/ota_backend_arduino_libretiny.cpp +22 -7
- esphome/components/ota/ota_backend_arduino_rp2040.cpp +22 -7
- esphome/components/pmsa003i/pmsa003i.cpp +9 -0
- esphome/components/qspi_amoled/display.py +16 -4
- esphome/components/qspi_amoled/qspi_amoled.cpp +16 -0
- esphome/components/qspi_amoled/qspi_amoled.h +0 -3
- esphome/components/remote_base/dooya_protocol.cpp +4 -4
- esphome/components/remote_base/rc_switch_protocol.cpp +1 -1
- esphome/components/restart/button/__init__.py +2 -0
- esphome/components/script/__init__.py +1 -1
- esphome/components/sensor/__init__.py +2 -0
- esphome/components/tuya/tuya.cpp +8 -2
- esphome/components/tuya/tuya.h +3 -1
- esphome/components/uart/__init__.py +72 -9
- esphome/components/uart/uart_component_esp32_arduino.cpp +18 -4
- esphome/components/uart/uart_component_esp_idf.cpp +22 -2
- esphome/components/uart/uart_component_host.cpp +295 -0
- esphome/components/uart/uart_component_host.h +38 -0
- esphome/components/uptime/sensor.py +44 -11
- esphome/components/uptime/{uptime_sensor.cpp → uptime_seconds_sensor.cpp} +11 -7
- esphome/components/uptime/{uptime_sensor.h → uptime_seconds_sensor.h} +2 -2
- esphome/components/uptime/uptime_timestamp_sensor.cpp +39 -0
- esphome/components/uptime/uptime_timestamp_sensor.h +30 -0
- esphome/components/veml7700/veml7700.cpp +1 -1
- esphome/components/veml7700/veml7700.h +5 -5
- esphome/components/voice_assistant/voice_assistant.cpp +4 -2
- esphome/components/web_server/server_index_v2.h +42 -41
- esphome/components/web_server/server_index_v3.h +368 -367
- esphome/components/wifi/wifi_component_esp_idf.cpp +1 -1
- esphome/components/wifi/wifi_component_pico_w.cpp +18 -2
- esphome/components/wireguard/__init__.py +1 -1
- esphome/components/x9c/output.py +7 -1
- esphome/const.py +2 -1
- esphome/core/defines.h +1 -0
- esphome/core/helpers.cpp +2 -2
- esphome/core/helpers.h +1 -1
- esphome/external_files.py +26 -0
- {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/METADATA +1 -1
- {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/RECORD +101 -95
- esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h +0 -493
- {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/LICENSE +0 -0
- {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/WHEEL +0 -0
- {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/entry_points.txt +0 -0
- {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/top_level.txt +0 -0
@@ -1,21 +1,18 @@
|
|
1
1
|
#pragma once
|
2
2
|
|
3
|
-
/**
|
4
|
-
* This is a workaround until we can figure out a way to get
|
5
|
-
* the tflite-micro idf component code available in CI
|
6
|
-
*
|
7
|
-
* */
|
8
|
-
//
|
9
|
-
#ifndef CLANG_TIDY
|
10
|
-
|
11
3
|
#ifdef USE_ESP_IDF
|
12
4
|
|
5
|
+
#include "preprocessor_settings.h"
|
6
|
+
#include "streaming_model.h"
|
7
|
+
|
13
8
|
#include "esphome/core/automation.h"
|
14
9
|
#include "esphome/core/component.h"
|
15
10
|
#include "esphome/core/ring_buffer.h"
|
16
11
|
|
17
12
|
#include "esphome/components/microphone/microphone.h"
|
18
13
|
|
14
|
+
#include <frontend_util.h>
|
15
|
+
|
19
16
|
#include <tensorflow/lite/core/c/common.h>
|
20
17
|
#include <tensorflow/lite/micro/micro_interpreter.h>
|
21
18
|
#include <tensorflow/lite/micro/micro_mutable_op_resolver.h>
|
@@ -23,35 +20,6 @@
|
|
23
20
|
namespace esphome {
|
24
21
|
namespace micro_wake_word {
|
25
22
|
|
26
|
-
// The following are dictated by the preprocessor model
|
27
|
-
//
|
28
|
-
// The number of features the audio preprocessor generates per slice
|
29
|
-
static const uint8_t PREPROCESSOR_FEATURE_SIZE = 40;
|
30
|
-
// How frequently the preprocessor generates a new set of features
|
31
|
-
static const uint8_t FEATURE_STRIDE_MS = 20;
|
32
|
-
// Duration of each slice used as input into the preprocessor
|
33
|
-
static const uint8_t FEATURE_DURATION_MS = 30;
|
34
|
-
// Audio sample frequency in hertz
|
35
|
-
static const uint16_t AUDIO_SAMPLE_FREQUENCY = 16000;
|
36
|
-
// The number of old audio samples that are saved to be part of the next feature window
|
37
|
-
static const uint16_t HISTORY_SAMPLES_TO_KEEP =
|
38
|
-
((FEATURE_DURATION_MS - FEATURE_STRIDE_MS) * (AUDIO_SAMPLE_FREQUENCY / 1000));
|
39
|
-
// The number of new audio samples to receive to be included with the next feature window
|
40
|
-
static const uint16_t NEW_SAMPLES_TO_GET = (FEATURE_STRIDE_MS * (AUDIO_SAMPLE_FREQUENCY / 1000));
|
41
|
-
// The total number of audio samples included in the feature window
|
42
|
-
static const uint16_t SAMPLE_DURATION_COUNT = FEATURE_DURATION_MS * AUDIO_SAMPLE_FREQUENCY / 1000;
|
43
|
-
// Number of bytes in memory needed for the preprocessor arena
|
44
|
-
static const uint32_t PREPROCESSOR_ARENA_SIZE = 9528;
|
45
|
-
|
46
|
-
// The following configure the streaming wake word model
|
47
|
-
//
|
48
|
-
// The number of audio slices to process before accepting a positive detection
|
49
|
-
static const uint8_t MIN_SLICES_BEFORE_DETECTION = 74;
|
50
|
-
|
51
|
-
// Number of bytes in memory needed for the streaming wake word model
|
52
|
-
static const uint32_t STREAMING_MODEL_ARENA_SIZE = 64000;
|
53
|
-
static const uint32_t STREAMING_MODEL_VARIABLE_ARENA_SIZE = 1024;
|
54
|
-
|
55
23
|
enum State {
|
56
24
|
IDLE,
|
57
25
|
START_MICROPHONE,
|
@@ -61,6 +29,9 @@ enum State {
|
|
61
29
|
STOPPING_MICROPHONE,
|
62
30
|
};
|
63
31
|
|
32
|
+
// The number of audio slices to process before accepting a positive detection
|
33
|
+
static const uint8_t MIN_SLICES_BEFORE_DETECTION = 74;
|
34
|
+
|
64
35
|
class MicroWakeWord : public Component {
|
65
36
|
public:
|
66
37
|
void setup() override;
|
@@ -73,28 +44,21 @@ class MicroWakeWord : public Component {
|
|
73
44
|
|
74
45
|
bool is_running() const { return this->state_ != State::IDLE; }
|
75
46
|
|
76
|
-
|
77
|
-
|
78
|
-
std::string get_wake_word() { return this->wake_word_; }
|
79
|
-
|
80
|
-
// Increasing either of these will reduce the rate of false acceptances while increasing the false rejection rate
|
81
|
-
void set_probability_cutoff(float probability_cutoff) { this->probability_cutoff_ = probability_cutoff; }
|
82
|
-
void set_sliding_window_average_size(size_t size);
|
47
|
+
void set_features_step_size(uint8_t step_size) { this->features_step_size_ = step_size; }
|
83
48
|
|
84
49
|
void set_microphone(microphone::Microphone *microphone) { this->microphone_ = microphone; }
|
85
50
|
|
86
51
|
Trigger<std::string> *get_wake_word_detected_trigger() const { return this->wake_word_detected_trigger_; }
|
87
52
|
|
88
|
-
void
|
89
|
-
|
90
|
-
|
91
|
-
protected:
|
92
|
-
void set_state_(State state);
|
93
|
-
int read_microphone_();
|
53
|
+
void add_wake_word_model(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_average_size,
|
54
|
+
const std::string &wake_word, size_t tensor_arena_size);
|
94
55
|
|
95
|
-
|
96
|
-
|
56
|
+
#ifdef USE_MICRO_WAKE_WORD_VAD
|
57
|
+
void add_vad_model(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size,
|
58
|
+
size_t tensor_arena_size);
|
59
|
+
#endif
|
97
60
|
|
61
|
+
protected:
|
98
62
|
microphone::Microphone *microphone_{nullptr};
|
99
63
|
Trigger<std::string> *wake_word_detected_trigger_ = new Trigger<std::string>();
|
100
64
|
State state_{State::IDLE};
|
@@ -102,85 +66,93 @@ class MicroWakeWord : public Component {
|
|
102
66
|
|
103
67
|
std::unique_ptr<RingBuffer> ring_buffer_;
|
104
68
|
|
105
|
-
|
69
|
+
std::vector<WakeWordModel> wake_word_models_;
|
106
70
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
tflite::MicroInterpreter *preprocessor_interperter_{nullptr};
|
71
|
+
#ifdef USE_MICRO_WAKE_WORD_VAD
|
72
|
+
std::unique_ptr<VADModel> vad_model_;
|
73
|
+
#endif
|
111
74
|
|
112
|
-
|
113
|
-
size_t last_n_index_{0};
|
75
|
+
tflite::MicroMutableOpResolver<20> streaming_op_resolver_;
|
114
76
|
|
115
|
-
|
116
|
-
|
77
|
+
// Audio frontend handles generating spectrogram features
|
78
|
+
struct FrontendConfig frontend_config_;
|
79
|
+
struct FrontendState frontend_state_;
|
117
80
|
|
118
|
-
// When the wake word detection first starts
|
119
|
-
// feature slices before accepting a positive detection
|
81
|
+
// When the wake word detection first starts, we ignore this many audio
|
82
|
+
// feature slices before accepting a positive detection
|
120
83
|
int16_t ignore_windows_{-MIN_SLICES_BEFORE_DETECTION};
|
121
84
|
|
122
|
-
uint8_t
|
123
|
-
uint8_t *streaming_tensor_arena_{nullptr};
|
124
|
-
uint8_t *preprocessor_tensor_arena_{nullptr};
|
125
|
-
int8_t *new_features_data_{nullptr};
|
126
|
-
|
127
|
-
tflite::MicroResourceVariables *mrv_{nullptr};
|
85
|
+
uint8_t features_step_size_;
|
128
86
|
|
129
|
-
// Stores audio
|
130
|
-
int16_t *
|
87
|
+
// Stores audio read from the microphone before being added to the ring buffer.
|
88
|
+
int16_t *input_buffer_{nullptr};
|
89
|
+
// Stores audio to be fed into the audio frontend for generating features.
|
90
|
+
int16_t *preprocessor_audio_buffer_{nullptr};
|
131
91
|
|
132
92
|
bool detected_{false};
|
93
|
+
std::string detected_wake_word_{""};
|
133
94
|
|
134
|
-
|
135
|
-
*
|
136
|
-
* If enough audio samples are available, it will generate one slice of new features.
|
137
|
-
* If the streaming model predicts the wake word, then the nonstreaming model confirms it.
|
138
|
-
* @param ring_Buffer Ring buffer containing raw audio samples
|
139
|
-
* @return True if the wake word is detected, false otherwise
|
140
|
-
*/
|
141
|
-
bool detect_wake_word_();
|
95
|
+
void set_state_(State state);
|
142
96
|
|
143
|
-
/// @brief
|
144
|
-
|
97
|
+
/// @brief Tests if there are enough samples in the ring buffer to generate new features.
|
98
|
+
/// @return True if enough samples, false otherwise.
|
99
|
+
bool has_enough_samples_();
|
145
100
|
|
146
|
-
/**
|
101
|
+
/** Reads audio from microphone into the ring buffer
|
147
102
|
*
|
148
|
-
*
|
149
|
-
*
|
103
|
+
* Audio data (16000 kHz with int16 samples) is read into the input_buffer_.
|
104
|
+
* Verifies the ring buffer has enough space for all audio data. If not, it logs
|
105
|
+
* a warning and resets the ring buffer entirely.
|
106
|
+
* @return Number of bytes written to the ring buffer
|
150
107
|
*/
|
151
|
-
|
108
|
+
size_t read_microphone_();
|
109
|
+
|
110
|
+
/// @brief Allocates memory for input_buffer_, preprocessor_audio_buffer_, and ring_buffer_
|
111
|
+
/// @return True if successful, false otherwise
|
112
|
+
bool allocate_buffers_();
|
113
|
+
|
114
|
+
/// @brief Frees memory allocated for input_buffer_ and preprocessor_audio_buffer_
|
115
|
+
void deallocate_buffers_();
|
152
116
|
|
153
|
-
|
117
|
+
/// @brief Loads streaming models and prepares the feature generation frontend
|
118
|
+
/// @return True if successful, false otherwise
|
119
|
+
bool load_models_();
|
120
|
+
|
121
|
+
/// @brief Deletes each model's TFLite interpreters and frees tensor arena memory. Frees memory used by the feature
|
122
|
+
/// generation frontend.
|
123
|
+
void unload_models_();
|
124
|
+
|
125
|
+
/** Performs inference with each configured model
|
154
126
|
*
|
155
|
-
*
|
156
|
-
*
|
157
|
-
* @param audio_data_size The number of samples to use as input to the preprocessor model
|
158
|
-
* @param feature_output Array that will store the features
|
159
|
-
* @return True if successful, false otherwise.
|
127
|
+
* If enough audio samples are available, it will generate one slice of new features.
|
128
|
+
* It then loops through and performs inference with each of the loaded models.
|
160
129
|
*/
|
161
|
-
|
162
|
-
int8_t feature_output[PREPROCESSOR_FEATURE_SIZE]);
|
130
|
+
void update_model_probabilities_();
|
163
131
|
|
164
|
-
/**
|
132
|
+
/** Checks every model's recent probabilities to determine if the wake word has been predicted
|
165
133
|
*
|
166
|
-
*
|
134
|
+
* Verifies the models have processed enough new samples for accurate predictions.
|
135
|
+
* Sets detected_wake_word_ to the wake word, if one is detected.
|
136
|
+
* @return True if a wake word is predicted, false otherwise
|
167
137
|
*/
|
168
|
-
|
138
|
+
bool detect_wake_words_();
|
169
139
|
|
170
|
-
/**
|
140
|
+
/** Generates features for a window of audio samples
|
171
141
|
*
|
172
|
-
*
|
173
|
-
*
|
174
|
-
* @param
|
175
|
-
* @return True if successful, false otherwise
|
142
|
+
* Reads samples from the ring buffer and feeds them into the preprocessor frontend.
|
143
|
+
* Adapted from TFLite microspeech frontend.
|
144
|
+
* @param features int8_t array to store the audio features
|
145
|
+
* @return True if successful, false otherwise.
|
176
146
|
*/
|
177
|
-
bool
|
147
|
+
bool generate_features_for_window_(int8_t features[PREPROCESSOR_FEATURE_SIZE]);
|
178
148
|
|
179
|
-
/// @brief
|
180
|
-
|
149
|
+
/// @brief Resets the ring buffer, ignore_windows_, and sliding window probabilities
|
150
|
+
void reset_states_();
|
181
151
|
|
182
152
|
/// @brief Returns true if successfully registered the streaming model's TensorFlow operations
|
183
|
-
bool register_streaming_ops_(tflite::MicroMutableOpResolver<
|
153
|
+
bool register_streaming_ops_(tflite::MicroMutableOpResolver<20> &op_resolver);
|
154
|
+
|
155
|
+
inline uint16_t new_samples_to_get_() { return (this->features_step_size_ * (AUDIO_SAMPLE_FREQUENCY / 1000)); }
|
184
156
|
};
|
185
157
|
|
186
158
|
template<typename... Ts> class StartAction : public Action<Ts...>, public Parented<MicroWakeWord> {
|
@@ -202,5 +174,3 @@ template<typename... Ts> class IsRunningCondition : public Condition<Ts...>, pub
|
|
202
174
|
} // namespace esphome
|
203
175
|
|
204
176
|
#endif // USE_ESP_IDF
|
205
|
-
|
206
|
-
#endif // CLANG_TIDY
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#ifdef USE_ESP_IDF
|
4
|
+
|
5
|
+
#include <cstdint>
|
6
|
+
|
7
|
+
namespace esphome {
|
8
|
+
namespace micro_wake_word {
|
9
|
+
|
10
|
+
// The number of features the audio preprocessor generates per slice
|
11
|
+
static const uint8_t PREPROCESSOR_FEATURE_SIZE = 40;
|
12
|
+
// Duration of each slice used as input into the preprocessor
|
13
|
+
static const uint8_t FEATURE_DURATION_MS = 30;
|
14
|
+
// Audio sample frequency in hertz
|
15
|
+
static const uint16_t AUDIO_SAMPLE_FREQUENCY = 16000;
|
16
|
+
|
17
|
+
} // namespace micro_wake_word
|
18
|
+
} // namespace esphome
|
19
|
+
|
20
|
+
#endif
|
@@ -0,0 +1,189 @@
|
|
1
|
+
#ifdef USE_ESP_IDF
|
2
|
+
|
3
|
+
#include "streaming_model.h"
|
4
|
+
|
5
|
+
#include "esphome/core/hal.h"
|
6
|
+
#include "esphome/core/helpers.h"
|
7
|
+
#include "esphome/core/log.h"
|
8
|
+
|
9
|
+
static const char *const TAG = "micro_wake_word";
|
10
|
+
|
11
|
+
namespace esphome {
|
12
|
+
namespace micro_wake_word {
|
13
|
+
|
14
|
+
void WakeWordModel::log_model_config() {
|
15
|
+
ESP_LOGCONFIG(TAG, " - Wake Word: %s", this->wake_word_.c_str());
|
16
|
+
ESP_LOGCONFIG(TAG, " Probability cutoff: %.3f", this->probability_cutoff_);
|
17
|
+
ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_size_);
|
18
|
+
}
|
19
|
+
|
20
|
+
void VADModel::log_model_config() {
|
21
|
+
ESP_LOGCONFIG(TAG, " - VAD Model");
|
22
|
+
ESP_LOGCONFIG(TAG, " Probability cutoff: %.3f", this->probability_cutoff_);
|
23
|
+
ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_size_);
|
24
|
+
}
|
25
|
+
|
26
|
+
bool StreamingModel::load_model(tflite::MicroMutableOpResolver<20> &op_resolver) {
|
27
|
+
ExternalRAMAllocator<uint8_t> arena_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
28
|
+
|
29
|
+
if (this->tensor_arena_ == nullptr) {
|
30
|
+
this->tensor_arena_ = arena_allocator.allocate(this->tensor_arena_size_);
|
31
|
+
if (this->tensor_arena_ == nullptr) {
|
32
|
+
ESP_LOGE(TAG, "Could not allocate the streaming model's tensor arena.");
|
33
|
+
return false;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
if (this->var_arena_ == nullptr) {
|
38
|
+
this->var_arena_ = arena_allocator.allocate(STREAMING_MODEL_VARIABLE_ARENA_SIZE);
|
39
|
+
if (this->var_arena_ == nullptr) {
|
40
|
+
ESP_LOGE(TAG, "Could not allocate the streaming model's variable tensor arena.");
|
41
|
+
return false;
|
42
|
+
}
|
43
|
+
this->ma_ = tflite::MicroAllocator::Create(this->var_arena_, STREAMING_MODEL_VARIABLE_ARENA_SIZE);
|
44
|
+
this->mrv_ = tflite::MicroResourceVariables::Create(this->ma_, 20);
|
45
|
+
}
|
46
|
+
|
47
|
+
const tflite::Model *model = tflite::GetModel(this->model_start_);
|
48
|
+
if (model->version() != TFLITE_SCHEMA_VERSION) {
|
49
|
+
ESP_LOGE(TAG, "Streaming model's schema is not supported");
|
50
|
+
return false;
|
51
|
+
}
|
52
|
+
|
53
|
+
if (this->interpreter_ == nullptr) {
|
54
|
+
this->interpreter_ = make_unique<tflite::MicroInterpreter>(
|
55
|
+
tflite::GetModel(this->model_start_), op_resolver, this->tensor_arena_, this->tensor_arena_size_, this->mrv_);
|
56
|
+
if (this->interpreter_->AllocateTensors() != kTfLiteOk) {
|
57
|
+
ESP_LOGE(TAG, "Failed to allocate tensors for the streaming model");
|
58
|
+
return false;
|
59
|
+
}
|
60
|
+
|
61
|
+
// Verify input tensor matches expected values
|
62
|
+
// Dimension 3 will represent the first layer stride, so skip it may vary
|
63
|
+
TfLiteTensor *input = this->interpreter_->input(0);
|
64
|
+
if ((input->dims->size != 3) || (input->dims->data[0] != 1) ||
|
65
|
+
(input->dims->data[2] != PREPROCESSOR_FEATURE_SIZE)) {
|
66
|
+
ESP_LOGE(TAG, "Streaming model tensor input dimensions has improper dimensions.");
|
67
|
+
return false;
|
68
|
+
}
|
69
|
+
|
70
|
+
if (input->type != kTfLiteInt8) {
|
71
|
+
ESP_LOGE(TAG, "Streaming model tensor input is not int8.");
|
72
|
+
return false;
|
73
|
+
}
|
74
|
+
|
75
|
+
// Verify output tensor matches expected values
|
76
|
+
TfLiteTensor *output = this->interpreter_->output(0);
|
77
|
+
if ((output->dims->size != 2) || (output->dims->data[0] != 1) || (output->dims->data[1] != 1)) {
|
78
|
+
ESP_LOGE(TAG, "Streaming model tensor output dimension is not 1x1.");
|
79
|
+
}
|
80
|
+
|
81
|
+
if (output->type != kTfLiteUInt8) {
|
82
|
+
ESP_LOGE(TAG, "Streaming model tensor output is not uint8.");
|
83
|
+
return false;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
return true;
|
88
|
+
}
|
89
|
+
|
90
|
+
void StreamingModel::unload_model() {
|
91
|
+
this->interpreter_.reset();
|
92
|
+
|
93
|
+
ExternalRAMAllocator<uint8_t> arena_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
94
|
+
|
95
|
+
arena_allocator.deallocate(this->tensor_arena_, this->tensor_arena_size_);
|
96
|
+
this->tensor_arena_ = nullptr;
|
97
|
+
arena_allocator.deallocate(this->var_arena_, STREAMING_MODEL_VARIABLE_ARENA_SIZE);
|
98
|
+
this->var_arena_ = nullptr;
|
99
|
+
}
|
100
|
+
|
101
|
+
bool StreamingModel::perform_streaming_inference(const int8_t features[PREPROCESSOR_FEATURE_SIZE]) {
|
102
|
+
if (this->interpreter_ != nullptr) {
|
103
|
+
TfLiteTensor *input = this->interpreter_->input(0);
|
104
|
+
|
105
|
+
std::memmove(
|
106
|
+
(int8_t *) (tflite::GetTensorData<int8_t>(input)) + PREPROCESSOR_FEATURE_SIZE * this->current_stride_step_,
|
107
|
+
features, PREPROCESSOR_FEATURE_SIZE);
|
108
|
+
++this->current_stride_step_;
|
109
|
+
|
110
|
+
uint8_t stride = this->interpreter_->input(0)->dims->data[1];
|
111
|
+
|
112
|
+
if (this->current_stride_step_ >= stride) {
|
113
|
+
this->current_stride_step_ = 0;
|
114
|
+
|
115
|
+
TfLiteStatus invoke_status = this->interpreter_->Invoke();
|
116
|
+
if (invoke_status != kTfLiteOk) {
|
117
|
+
ESP_LOGW(TAG, "Streaming interpreter invoke failed");
|
118
|
+
return false;
|
119
|
+
}
|
120
|
+
|
121
|
+
TfLiteTensor *output = this->interpreter_->output(0);
|
122
|
+
|
123
|
+
++this->last_n_index_;
|
124
|
+
if (this->last_n_index_ == this->sliding_window_size_)
|
125
|
+
this->last_n_index_ = 0;
|
126
|
+
this->recent_streaming_probabilities_[this->last_n_index_] = output->data.uint8[0]; // probability;
|
127
|
+
}
|
128
|
+
return true;
|
129
|
+
}
|
130
|
+
ESP_LOGE(TAG, "Streaming interpreter is not initialized.");
|
131
|
+
return false;
|
132
|
+
}
|
133
|
+
|
134
|
+
void StreamingModel::reset_probabilities() {
|
135
|
+
for (auto &prob : this->recent_streaming_probabilities_) {
|
136
|
+
prob = 0;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
WakeWordModel::WakeWordModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_average_size,
|
141
|
+
const std::string &wake_word, size_t tensor_arena_size) {
|
142
|
+
this->model_start_ = model_start;
|
143
|
+
this->probability_cutoff_ = probability_cutoff;
|
144
|
+
this->sliding_window_size_ = sliding_window_average_size;
|
145
|
+
this->recent_streaming_probabilities_.resize(sliding_window_average_size, 0);
|
146
|
+
this->wake_word_ = wake_word;
|
147
|
+
this->tensor_arena_size_ = tensor_arena_size;
|
148
|
+
};
|
149
|
+
|
150
|
+
bool WakeWordModel::determine_detected() {
|
151
|
+
int32_t sum = 0;
|
152
|
+
for (auto &prob : this->recent_streaming_probabilities_) {
|
153
|
+
sum += prob;
|
154
|
+
}
|
155
|
+
|
156
|
+
float sliding_window_average = static_cast<float>(sum) / static_cast<float>(255 * this->sliding_window_size_);
|
157
|
+
|
158
|
+
// Detect the wake word if the sliding window average is above the cutoff
|
159
|
+
if (sliding_window_average > this->probability_cutoff_) {
|
160
|
+
ESP_LOGD(TAG, "The '%s' model sliding average probability is %.3f and most recent probability is %.3f",
|
161
|
+
this->wake_word_.c_str(), sliding_window_average,
|
162
|
+
this->recent_streaming_probabilities_[this->last_n_index_] / (255.0));
|
163
|
+
return true;
|
164
|
+
}
|
165
|
+
return false;
|
166
|
+
}
|
167
|
+
|
168
|
+
VADModel::VADModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size,
|
169
|
+
size_t tensor_arena_size) {
|
170
|
+
this->model_start_ = model_start;
|
171
|
+
this->probability_cutoff_ = probability_cutoff;
|
172
|
+
this->sliding_window_size_ = sliding_window_size;
|
173
|
+
this->recent_streaming_probabilities_.resize(sliding_window_size, 0);
|
174
|
+
this->tensor_arena_size_ = tensor_arena_size;
|
175
|
+
};
|
176
|
+
|
177
|
+
bool VADModel::determine_detected() {
|
178
|
+
uint8_t max = 0;
|
179
|
+
for (auto &prob : this->recent_streaming_probabilities_) {
|
180
|
+
max = std::max(prob, max);
|
181
|
+
}
|
182
|
+
|
183
|
+
return max > this->probability_cutoff_;
|
184
|
+
}
|
185
|
+
|
186
|
+
} // namespace micro_wake_word
|
187
|
+
} // namespace esphome
|
188
|
+
|
189
|
+
#endif
|
@@ -0,0 +1,84 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#ifdef USE_ESP_IDF
|
4
|
+
|
5
|
+
#include "preprocessor_settings.h"
|
6
|
+
|
7
|
+
#include <tensorflow/lite/core/c/common.h>
|
8
|
+
#include <tensorflow/lite/micro/micro_interpreter.h>
|
9
|
+
#include <tensorflow/lite/micro/micro_mutable_op_resolver.h>
|
10
|
+
|
11
|
+
namespace esphome {
|
12
|
+
namespace micro_wake_word {
|
13
|
+
|
14
|
+
static const uint32_t STREAMING_MODEL_VARIABLE_ARENA_SIZE = 1024;
|
15
|
+
|
16
|
+
class StreamingModel {
|
17
|
+
public:
|
18
|
+
virtual void log_model_config() = 0;
|
19
|
+
virtual bool determine_detected() = 0;
|
20
|
+
|
21
|
+
bool perform_streaming_inference(const int8_t features[PREPROCESSOR_FEATURE_SIZE]);
|
22
|
+
|
23
|
+
/// @brief Sets all recent_streaming_probabilities to 0
|
24
|
+
void reset_probabilities();
|
25
|
+
|
26
|
+
/// @brief Allocates tensor and variable arenas and sets up the model interpreter
|
27
|
+
/// @param op_resolver MicroMutableOpResolver object that must exist until the model is unloaded
|
28
|
+
/// @return True if successful, false otherwise
|
29
|
+
bool load_model(tflite::MicroMutableOpResolver<20> &op_resolver);
|
30
|
+
|
31
|
+
/// @brief Destroys the TFLite interpreter and frees the tensor and variable arenas' memory
|
32
|
+
void unload_model();
|
33
|
+
|
34
|
+
protected:
|
35
|
+
uint8_t current_stride_step_{0};
|
36
|
+
|
37
|
+
float probability_cutoff_;
|
38
|
+
size_t sliding_window_size_;
|
39
|
+
size_t last_n_index_{0};
|
40
|
+
size_t tensor_arena_size_;
|
41
|
+
std::vector<uint8_t> recent_streaming_probabilities_;
|
42
|
+
|
43
|
+
const uint8_t *model_start_;
|
44
|
+
uint8_t *tensor_arena_{nullptr};
|
45
|
+
uint8_t *var_arena_{nullptr};
|
46
|
+
std::unique_ptr<tflite::MicroInterpreter> interpreter_;
|
47
|
+
tflite::MicroResourceVariables *mrv_{nullptr};
|
48
|
+
tflite::MicroAllocator *ma_{nullptr};
|
49
|
+
};
|
50
|
+
|
51
|
+
class WakeWordModel final : public StreamingModel {
|
52
|
+
public:
|
53
|
+
WakeWordModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_average_size,
|
54
|
+
const std::string &wake_word, size_t tensor_arena_size);
|
55
|
+
|
56
|
+
void log_model_config() override;
|
57
|
+
|
58
|
+
/// @brief Checks for the wake word by comparing the mean probability in the sliding window with the probability
|
59
|
+
/// cutoff
|
60
|
+
/// @return True if wake word is detected, false otherwise
|
61
|
+
bool determine_detected() override;
|
62
|
+
|
63
|
+
const std::string &get_wake_word() const { return this->wake_word_; }
|
64
|
+
|
65
|
+
protected:
|
66
|
+
std::string wake_word_;
|
67
|
+
};
|
68
|
+
|
69
|
+
class VADModel final : public StreamingModel {
|
70
|
+
public:
|
71
|
+
VADModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size, size_t tensor_arena_size);
|
72
|
+
|
73
|
+
void log_model_config() override;
|
74
|
+
|
75
|
+
/// @brief Checks for voice activity by comparing the max probability in the sliding window with the probability
|
76
|
+
/// cutoff
|
77
|
+
/// @return True if voice activity is detected, false otherwise
|
78
|
+
bool determine_detected() override;
|
79
|
+
};
|
80
|
+
|
81
|
+
} // namespace micro_wake_word
|
82
|
+
} // namespace esphome
|
83
|
+
|
84
|
+
#endif
|
@@ -52,6 +52,7 @@ const uint8_t MITSUBISHI_BYTE16 = 0X00;
|
|
52
52
|
|
53
53
|
climate::ClimateTraits MitsubishiClimate::traits() {
|
54
54
|
auto traits = climate::ClimateTraits();
|
55
|
+
traits.set_supports_current_temperature(this->sensor_ != nullptr);
|
55
56
|
traits.set_supports_action(false);
|
56
57
|
traits.set_visual_min_temperature(MITSUBISHI_TEMP_MIN);
|
57
58
|
traits.set_visual_max_temperature(MITSUBISHI_TEMP_MAX);
|
@@ -37,6 +37,7 @@ RAW_ENCODING = {
|
|
37
37
|
"NONE": RawEncoding.NONE,
|
38
38
|
"HEXBYTES": RawEncoding.HEXBYTES,
|
39
39
|
"COMMA": RawEncoding.COMMA,
|
40
|
+
"ANSI": RawEncoding.ANSI,
|
40
41
|
}
|
41
42
|
|
42
43
|
CONFIG_SCHEMA = cv.All(
|
@@ -49,7 +50,7 @@ CONFIG_SCHEMA = cv.All(
|
|
49
50
|
cv.Optional(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE),
|
50
51
|
cv.Optional(CONF_REGISTER_COUNT, default=0): cv.positive_int,
|
51
52
|
cv.Optional(CONF_RESPONSE_SIZE, default=2): cv.positive_int,
|
52
|
-
cv.Optional(CONF_RAW_ENCODE, default="
|
53
|
+
cv.Optional(CONF_RAW_ENCODE, default="ANSI"): cv.enum(RAW_ENCODING),
|
53
54
|
}
|
54
55
|
),
|
55
56
|
validate_modbus_register,
|
@@ -27,8 +27,11 @@ void ModbusTextSensor::parse_and_publish(const std::vector<uint8_t> &data) {
|
|
27
27
|
sprintf(buffer, index != this->offset ? ",%d" : "%d", b);
|
28
28
|
output << buffer;
|
29
29
|
break;
|
30
|
+
case RawEncoding::ANSI:
|
31
|
+
if (b < 0x20)
|
32
|
+
break;
|
33
|
+
// FALLTHROUGH
|
30
34
|
// Anything else no encoding
|
31
|
-
case RawEncoding::NONE:
|
32
35
|
default:
|
33
36
|
output << (char) b;
|
34
37
|
break;
|
@@ -9,7 +9,7 @@
|
|
9
9
|
namespace esphome {
|
10
10
|
namespace modbus_controller {
|
11
11
|
|
12
|
-
enum class RawEncoding { NONE = 0, HEXBYTES = 1, COMMA = 2 };
|
12
|
+
enum class RawEncoding { NONE = 0, HEXBYTES = 1, COMMA = 2, ANSI = 3 };
|
13
13
|
|
14
14
|
class ModbusTextSensor : public Component, public text_sensor::TextSensor, public SensorItem {
|
15
15
|
public:
|
@@ -26,6 +26,7 @@ from esphome.const import (
|
|
26
26
|
DEVICE_CLASS_BATTERY,
|
27
27
|
DEVICE_CLASS_CARBON_DIOXIDE,
|
28
28
|
DEVICE_CLASS_CARBON_MONOXIDE,
|
29
|
+
DEVICE_CLASS_CONDUCTIVITY,
|
29
30
|
DEVICE_CLASS_CURRENT,
|
30
31
|
DEVICE_CLASS_DATA_RATE,
|
31
32
|
DEVICE_CLASS_DATA_SIZE,
|
@@ -82,6 +83,7 @@ DEVICE_CLASSES = [
|
|
82
83
|
DEVICE_CLASS_BATTERY,
|
83
84
|
DEVICE_CLASS_CARBON_DIOXIDE,
|
84
85
|
DEVICE_CLASS_CARBON_MONOXIDE,
|
86
|
+
DEVICE_CLASS_CONDUCTIVITY,
|
85
87
|
DEVICE_CLASS_CURRENT,
|
86
88
|
DEVICE_CLASS_DATA_RATE,
|
87
89
|
DEVICE_CLASS_DATA_SIZE,
|