ESPHome  2024.12.2
bme68x_bsec2.cpp
Go to the documentation of this file.
1 #include "esphome/core/defines.h"
2 #include "esphome/core/helpers.h"
3 #include "esphome/core/log.h"
4 
5 #ifdef USE_BSEC2
6 #include "bme68x_bsec2.h"
7 
8 #include <string>
9 
10 namespace esphome {
11 namespace bme68x_bsec2 {
12 
13 #define BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(a) (a == ALGORITHM_OUTPUT_CLASSIFICATION ? "Classification" : "Regression")
14 #define BME68X_BSEC2_OPERATING_AGE_LOG(o) (o == OPERATING_AGE_4D ? "4 days" : "28 days")
15 #define BME68X_BSEC2_SAMPLE_RATE_LOG(r) (r == SAMPLE_RATE_DEFAULT ? "Default" : (r == SAMPLE_RATE_ULP ? "ULP" : "LP"))
16 #define BME68X_BSEC2_VOLTAGE_LOG(v) (v == VOLTAGE_3_3V ? "3.3V" : "1.8V")
17 
18 static const char *const TAG = "bme68x_bsec2.sensor";
19 
20 static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
21 
23  ESP_LOGCONFIG(TAG, "Setting up BME68X via BSEC2...");
24 
25  this->bsec_status_ = bsec_init_m(&this->bsec_instance_);
26  if (this->bsec_status_ != BSEC_OK) {
27  this->mark_failed();
28  ESP_LOGE(TAG, "bsec_init_m failed: status %d", this->bsec_status_);
29  return;
30  }
31 
32  bsec_get_version_m(&this->bsec_instance_, &this->version_);
33 
34  this->bme68x_status_ = bme68x_init(&this->bme68x_);
35  if (this->bme68x_status_ != BME68X_OK) {
36  this->mark_failed();
37  ESP_LOGE(TAG, "bme68x_init failed: status %d", this->bme68x_status_);
38  return;
39  }
40  if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) {
42  if (this->bsec_status_ != BSEC_OK) {
43  this->mark_failed();
44  ESP_LOGE(TAG, "bsec_set_configuration_m failed: status %d", this->bsec_status_);
45  return;
46  }
47  }
48 
49  this->update_subscription_();
50  if (this->bsec_status_ != BSEC_OK) {
51  this->mark_failed();
52  ESP_LOGE(TAG, "bsec_update_subscription_m failed: status %d", this->bsec_status_);
53  return;
54  }
55 
56  this->load_state_();
57 }
58 
60  ESP_LOGCONFIG(TAG, "BME68X via BSEC2:");
61 
62  ESP_LOGCONFIG(TAG, " BSEC2 version: %d.%d.%d.%d", this->version_.major, this->version_.minor,
63  this->version_.major_bugfix, this->version_.minor_bugfix);
64 
65  ESP_LOGCONFIG(TAG, " BSEC2 configuration blob:");
66  ESP_LOGCONFIG(TAG, " Configured: %s", YESNO(this->bsec2_blob_configured_));
67  if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) {
68  ESP_LOGCONFIG(TAG, " Size: %" PRIu32, this->bsec2_configuration_length_);
69  }
70 
71  if (this->is_failed()) {
72  ESP_LOGE(TAG, "Communication failed (BSEC2 status: %d, BME68X status: %d)", this->bsec_status_,
73  this->bme68x_status_);
74  }
75 
77  ESP_LOGCONFIG(TAG, " Algorithm output: %s", BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(this->algorithm_output_));
78  }
79  ESP_LOGCONFIG(TAG, " Operating age: %s", BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_));
80  ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_));
81  ESP_LOGCONFIG(TAG, " Voltage: %s", BME68X_BSEC2_VOLTAGE_LOG(this->voltage_));
82  ESP_LOGCONFIG(TAG, " State save interval: %ims", this->state_save_interval_ms_);
83  ESP_LOGCONFIG(TAG, " Temperature offset: %.2f", this->temperature_offset_);
84 
85 #ifdef USE_SENSOR
86  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
87  ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->temperature_sample_rate_));
88  LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
89  ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->pressure_sample_rate_));
90  LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
91  ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->humidity_sample_rate_));
92  LOG_SENSOR(" ", "Gas resistance", this->gas_resistance_sensor_);
93  LOG_SENSOR(" ", "CO2 equivalent", this->co2_equivalent_sensor_);
94  LOG_SENSOR(" ", "Breath VOC equivalent", this->breath_voc_equivalent_sensor_);
95  LOG_SENSOR(" ", "IAQ", this->iaq_sensor_);
96  LOG_SENSOR(" ", "IAQ static", this->iaq_static_sensor_);
97  LOG_SENSOR(" ", "Numeric IAQ accuracy", this->iaq_accuracy_sensor_);
98 #endif
99 #ifdef USE_TEXT_SENSOR
100  LOG_TEXT_SENSOR(" ", "IAQ accuracy", this->iaq_accuracy_text_sensor_);
101 #endif
102 }
103 
105 
107  this->run_();
108 
109  if (this->bsec_status_ < BSEC_OK || this->bme68x_status_ < BME68X_OK) {
110  this->status_set_error();
111  } else {
112  this->status_clear_error();
113  }
114  if (this->bsec_status_ > BSEC_OK || this->bme68x_status_ > BME68X_OK) {
115  this->status_set_warning();
116  } else {
117  this->status_clear_warning();
118  }
119  // Process a single action from the queue. These are primarily sensor state publishes
120  // that in totality take too long to send in a single call.
121  if (this->queue_.size()) {
122  auto action = std::move(this->queue_.front());
123  this->queue_.pop();
124  action();
125  }
126 }
127 
128 void BME68xBSEC2Component::set_config_(const uint8_t *config, uint32_t len) {
129  if (len > BSEC_MAX_PROPERTY_BLOB_SIZE) {
130  ESP_LOGE(TAG, "Configuration is larger than BSEC_MAX_PROPERTY_BLOB_SIZE");
131  this->mark_failed();
132  return;
133  }
134  uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE];
135  this->bsec_status_ = bsec_set_configuration_m(&this->bsec_instance_, config, len, work_buffer, sizeof(work_buffer));
136  if (this->bsec_status_ == BSEC_OK) {
137  this->bsec2_blob_configured_ = true;
138  }
139 }
140 
142  if (sample_rate == SAMPLE_RATE_DEFAULT) {
143  sample_rate = this->sample_rate_;
144  }
145  return sample_rate == SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
146 }
147 
149  bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
150  uint8_t num_virtual_sensors = 0;
151 #ifdef USE_SENSOR
152  if (this->iaq_sensor_) {
153  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_IAQ;
154  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
155  num_virtual_sensors++;
156  }
157 
158  if (this->iaq_static_sensor_) {
159  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_STATIC_IAQ;
160  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
161  num_virtual_sensors++;
162  }
163 
164  if (this->co2_equivalent_sensor_) {
165  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
166  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
167  num_virtual_sensors++;
168  }
169 
170  if (this->breath_voc_equivalent_sensor_) {
171  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
172  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
173  num_virtual_sensors++;
174  }
175 
176  if (this->pressure_sensor_) {
177  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
178  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->pressure_sample_rate_);
179  num_virtual_sensors++;
180  }
181 
182  if (this->gas_resistance_sensor_) {
183  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
184  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
185  num_virtual_sensors++;
186  }
187 
188  if (this->temperature_sensor_) {
189  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
190  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->temperature_sample_rate_);
191  num_virtual_sensors++;
192  }
193 
194  if (this->humidity_sensor_) {
195  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
196  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->humidity_sample_rate_);
197  num_virtual_sensors++;
198  }
199 #endif
200  bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
201  uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
202  this->bsec_status_ = bsec_update_subscription_m(&this->bsec_instance_, virtual_sensors, num_virtual_sensors,
203  sensor_settings, &num_sensor_settings);
204 }
205 
207  this->op_mode_ = this->bsec_settings_.op_mode;
208  int64_t curr_time_ns = this->get_time_ns_();
209  if (curr_time_ns < this->bsec_settings_.next_call) {
210  return;
211  }
212  uint8_t status;
213 
214  ESP_LOGV(TAG, "Performing sensor run");
215 
216  struct bme68x_conf bme68x_conf;
217  this->bsec_status_ = bsec_sensor_control_m(&this->bsec_instance_, curr_time_ns, &this->bsec_settings_);
218  if (this->bsec_status_ < BSEC_OK) {
219  ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_);
220  return;
221  }
222 
223  switch (this->bsec_settings_.op_mode) {
224  case BME68X_FORCED_MODE:
225  bme68x_get_conf(&bme68x_conf, &this->bme68x_);
226 
227  bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling;
228  bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling;
229  bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling;
230  bme68x_set_conf(&bme68x_conf, &this->bme68x_);
231  this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
232  this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature;
233  this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration;
234 
235  // status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_);
236  status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
237  status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_);
238  this->op_mode_ = BME68X_FORCED_MODE;
239  ESP_LOGV(TAG, "Using forced mode");
240 
241  break;
242  case BME68X_PARALLEL_MODE:
243  if (this->op_mode_ != this->bsec_settings_.op_mode) {
244  bme68x_get_conf(&bme68x_conf, &this->bme68x_);
245 
246  bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling;
247  bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling;
248  bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling;
249  bme68x_set_conf(&bme68x_conf, &this->bme68x_);
250 
251  this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
252  this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile;
253  this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile;
254  this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len;
255  this->bme68x_heatr_conf_.shared_heatr_dur =
256  BSEC_TOTAL_HEAT_DUR -
257  (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000));
258 
259  status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
260 
261  status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_);
262  this->op_mode_ = BME68X_PARALLEL_MODE;
263  ESP_LOGV(TAG, "Using parallel mode");
264  }
265  break;
266  case BME68X_SLEEP_MODE:
267  if (this->op_mode_ != this->bsec_settings_.op_mode) {
268  bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_);
269  this->op_mode_ = BME68X_SLEEP_MODE;
270  ESP_LOGV(TAG, "Using sleep mode");
271  }
272  break;
273  }
274 
275  if (this->bsec_settings_.trigger_measurement && this->bsec_settings_.op_mode != BME68X_SLEEP_MODE) {
276  uint32_t meas_dur = 0;
277  meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_);
278  ESP_LOGV(TAG, "Queueing read in %uus", meas_dur);
279  this->set_timeout("read", meas_dur / 1000, [this, curr_time_ns]() { this->read_(curr_time_ns); });
280  } else {
281  ESP_LOGV(TAG, "Measurement not required");
282  this->read_(curr_time_ns);
283  }
284 }
285 
286 void BME68xBSEC2Component::read_(int64_t trigger_time_ns) {
287  ESP_LOGV(TAG, "Reading data");
288 
289  if (this->bsec_settings_.trigger_measurement) {
290  uint8_t current_op_mode;
291  this->bme68x_status_ = bme68x_get_op_mode(&current_op_mode, &this->bme68x_);
292 
293  if (current_op_mode == BME68X_SLEEP_MODE) {
294  ESP_LOGV(TAG, "Still in sleep mode, doing nothing");
295  return;
296  }
297  }
298 
299  if (!this->bsec_settings_.process_data) {
300  ESP_LOGV(TAG, "Data processing not required");
301  return;
302  }
303 
304  struct bme68x_data data[3];
305  uint8_t nFields = 0;
306  this->bme68x_status_ = bme68x_get_data(this->op_mode_, &data[0], &nFields, &this->bme68x_);
307 
308  if (this->bme68x_status_ != BME68X_OK) {
309  ESP_LOGW(TAG, "Failed to get sensor data (BME68X error code %d)", this->bme68x_status_);
310  return;
311  }
312  if (nFields < 1) {
313  ESP_LOGD(TAG, "BME68X did not provide new data");
314  return;
315  }
316 
317  for (uint8_t i = 0; i < nFields; i++) {
318  bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temperature, Pressure, Humidity & Gas Resistance
319  uint8_t num_inputs = 0;
320 
321  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_TEMPERATURE)) {
322  inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
323  inputs[num_inputs].signal = data[i].temperature;
324  inputs[num_inputs].time_stamp = trigger_time_ns;
325  num_inputs++;
326  }
327  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_HEATSOURCE)) {
328  inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
329  inputs[num_inputs].signal = this->temperature_offset_;
330  inputs[num_inputs].time_stamp = trigger_time_ns;
331  num_inputs++;
332  }
333  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_HUMIDITY)) {
334  inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
335  inputs[num_inputs].signal = data[i].humidity;
336  inputs[num_inputs].time_stamp = trigger_time_ns;
337  num_inputs++;
338  }
339  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_PRESSURE)) {
340  inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
341  inputs[num_inputs].signal = data[i].pressure;
342  inputs[num_inputs].time_stamp = trigger_time_ns;
343  num_inputs++;
344  }
345  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_GASRESISTOR)) {
346  if (data[i].status & BME68X_GASM_VALID_MSK) {
347  inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
348  inputs[num_inputs].signal = data[i].gas_resistance;
349  inputs[num_inputs].time_stamp = trigger_time_ns;
350  num_inputs++;
351  } else {
352  ESP_LOGD(TAG, "BME68X did not report gas data");
353  }
354  }
355  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_PROFILE_PART) &&
356  (data[i].status & BME68X_GASM_VALID_MSK)) {
357  inputs[num_inputs].sensor_id = BSEC_INPUT_PROFILE_PART;
358  inputs[num_inputs].signal = (this->op_mode_ == BME68X_FORCED_MODE) ? 0 : data[i].gas_index;
359  inputs[num_inputs].time_stamp = trigger_time_ns;
360  num_inputs++;
361  }
362 
363  if (num_inputs < 1) {
364  ESP_LOGD(TAG, "No signal inputs available for BSEC2");
365  return;
366  }
367 
368  bsec_output_t outputs[BSEC_NUMBER_OUTPUTS];
369  uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
370  this->bsec_status_ = bsec_do_steps_m(&this->bsec_instance_, inputs, num_inputs, outputs, &num_outputs);
371  if (this->bsec_status_ != BSEC_OK) {
372  ESP_LOGW(TAG, "BSEC2 failed to process signals (BSEC2 error code %d)", this->bsec_status_);
373  return;
374  }
375  if (num_outputs < 1) {
376  ESP_LOGD(TAG, "No signal outputs provided by BSEC2");
377  return;
378  }
379 
380  this->publish_(outputs, num_outputs);
381  }
382 }
383 
384 void BME68xBSEC2Component::publish_(const bsec_output_t *outputs, uint8_t num_outputs) {
385  ESP_LOGV(TAG, "Publishing sensor states");
386  bool update_accuracy = false;
387  uint8_t max_accuracy = 0;
388  for (uint8_t i = 0; i < num_outputs; i++) {
389  float signal = outputs[i].signal;
390  switch (outputs[i].sensor_id) {
391  case BSEC_OUTPUT_IAQ:
392  max_accuracy = std::max(outputs[i].accuracy, max_accuracy);
393  update_accuracy = true;
394 #ifdef USE_SENSOR
395  this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_sensor_, signal); });
396 #endif
397  break;
398  case BSEC_OUTPUT_STATIC_IAQ:
399  max_accuracy = std::max(outputs[i].accuracy, max_accuracy);
400  update_accuracy = true;
401 #ifdef USE_SENSOR
402  this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_static_sensor_, signal); });
403 #endif
404  break;
405  case BSEC_OUTPUT_CO2_EQUIVALENT:
406 #ifdef USE_SENSOR
407  this->queue_push_([this, signal]() { this->publish_sensor_(this->co2_equivalent_sensor_, signal); });
408 #endif
409  break;
410  case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
411 #ifdef USE_SENSOR
412  this->queue_push_([this, signal]() { this->publish_sensor_(this->breath_voc_equivalent_sensor_, signal); });
413 #endif
414  break;
415  case BSEC_OUTPUT_RAW_PRESSURE:
416 #ifdef USE_SENSOR
417  this->queue_push_([this, signal]() { this->publish_sensor_(this->pressure_sensor_, signal / 100.0f); });
418 #endif
419  break;
420  case BSEC_OUTPUT_RAW_GAS:
421 #ifdef USE_SENSOR
422  this->queue_push_([this, signal]() { this->publish_sensor_(this->gas_resistance_sensor_, signal); });
423 #endif
424  break;
425  case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
426 #ifdef USE_SENSOR
427  this->queue_push_([this, signal]() { this->publish_sensor_(this->temperature_sensor_, signal); });
428 #endif
429  break;
430  case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
431 #ifdef USE_SENSOR
432  this->queue_push_([this, signal]() { this->publish_sensor_(this->humidity_sensor_, signal); });
433 #endif
434  break;
435  }
436  }
437  if (update_accuracy) {
438 #ifdef USE_SENSOR
439  this->queue_push_(
440  [this, max_accuracy]() { this->publish_sensor_(this->iaq_accuracy_sensor_, max_accuracy, true); });
441 #endif
442 #ifdef USE_TEXT_SENSOR
443  this->queue_push_([this, max_accuracy]() {
444  this->publish_sensor_(this->iaq_accuracy_text_sensor_, IAQ_ACCURACY_STATES[max_accuracy]);
445  });
446 #endif
447  // Queue up an opportunity to save state
448  this->queue_push_([this, max_accuracy]() { this->save_state_(max_accuracy); });
449  }
450 }
451 
453  int64_t time_ms = millis();
454  if (this->last_time_ms_ > time_ms) {
455  this->millis_overflow_counter_++;
456  }
457  this->last_time_ms_ = time_ms;
458 
459  return (time_ms + ((int64_t) this->millis_overflow_counter_ << 32)) * INT64_C(1000000);
460 }
461 
462 #ifdef USE_SENSOR
463 void BME68xBSEC2Component::publish_sensor_(sensor::Sensor *sensor, float value, bool change_only) {
464  if (!sensor || (change_only && sensor->has_state() && sensor->state == value)) {
465  return;
466  }
467  sensor->publish_state(value);
468 }
469 #endif
470 
471 #ifdef USE_TEXT_SENSOR
473  if (!sensor || (sensor->has_state() && sensor->state == value)) {
474  return;
475  }
476  sensor->publish_state(value);
477 }
478 #endif
479 
481  uint32_t hash = this->get_hash();
482  this->bsec_state_ = global_preferences->make_preference<uint8_t[BSEC_MAX_STATE_BLOB_SIZE]>(hash, true);
483 
484  uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
485  if (this->bsec_state_.load(&state)) {
486  ESP_LOGV(TAG, "Loading state");
487  uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE];
488  this->bsec_status_ =
489  bsec_set_state_m(&this->bsec_instance_, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer, sizeof(work_buffer));
490  if (this->bsec_status_ != BSEC_OK) {
491  ESP_LOGW(TAG, "Failed to load state (BSEC2 error code %d)", this->bsec_status_);
492  }
493  ESP_LOGI(TAG, "Loaded state");
494  }
495 }
496 
497 void BME68xBSEC2Component::save_state_(uint8_t accuracy) {
498  if (accuracy < 3 || (millis() - this->last_state_save_ms_ < this->state_save_interval_ms_)) {
499  return;
500  }
501 
502  ESP_LOGV(TAG, "Saving state");
503 
504  uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
505  uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE];
506  uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
507 
508  this->bsec_status_ = bsec_get_state_m(&this->bsec_instance_, 0, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer,
509  BSEC_MAX_STATE_BLOB_SIZE, &num_serialized_state);
510  if (this->bsec_status_ != BSEC_OK) {
511  ESP_LOGW(TAG, "Failed fetch state for save (BSEC2 error code %d)", this->bsec_status_);
512  return;
513  }
514 
515  if (!this->bsec_state_.save(&state)) {
516  ESP_LOGW(TAG, "Failed to save state");
517  return;
518  }
519  this->last_state_save_ms_ = millis();
520 
521  ESP_LOGI(TAG, "Saved state");
522 }
523 
524 } // namespace bme68x_bsec2
525 } // namespace esphome
526 #endif
void publish_(const bsec_output_t *outputs, uint8_t num_outputs)
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
void set_config_(const uint8_t *config, u_int32_t len)
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
void publish_sensor_(sensor::Sensor *sensor, float value, bool change_only=false)
bool is_failed() const
Definition: component.cpp:143
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
uint8_t bsec_instance_[BSEC_INSTANCE_SIZE]
Definition: bme68x_bsec2.h:112
void publish_state(const std::string &state)
Definition: text_sensor.cpp:9
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
bool save(const T *src)
Definition: preferences.h:21
float calc_sensor_sample_rate_(SampleRate sample_rate)
float state
This member variable stores the last state that has passed through all filters.
Definition: sensor.h:131
void status_set_error(const char *message="unspecified")
Definition: component.cpp:159
struct bme68x_heatr_conf bme68x_heatr_conf_
Definition: bme68x_bsec2.h:114
ESPPreferences * global_preferences
void status_clear_warning()
Definition: component.cpp:166
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
text_sensor::TextSensor * iaq_accuracy_text_sensor_
Definition: bme68x_bsec2.h:155
void status_clear_error()
Definition: component.cpp:172
uint8_t status
Definition: bl0942.h:74
std::string size_t len
Definition: helpers.h:293
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
void queue_push_(std::function< void()> &&f)
Definition: bme68x_bsec2.h:107
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
bool has_state() const
Return whether this sensor has gotten a full state (that passed through all filters) yet...
Definition: sensor.cpp:97
Base-class for all sensors.
Definition: sensor.h:57
void read_(int64_t trigger_time_ns)
std::queue< std::function< void()> > queue_
Definition: bme68x_bsec2.h:122
esphome::sensor::Sensor * sensor
Definition: statsd.h:38
bool state
Definition: fan.h:34