ESPHome  2024.5.5
binary_sensor_map.cpp
Go to the documentation of this file.
1 #include "binary_sensor_map.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace binary_sensor_map {
6 
7 static const char *const TAG = "binary_sensor_map";
8 
9 void BinarySensorMap::dump_config() { LOG_SENSOR(" ", "binary_sensor_map", this); }
10 
12  switch (this->sensor_type_) {
14  this->process_group_();
15  break;
17  this->process_sum_();
18  break;
20  this->process_bayesian_();
21  break;
22  }
23 }
24 
26  float total_current_value = 0.0;
27  uint8_t num_active_sensors = 0;
28  uint64_t mask = 0x00;
29 
30  // - check all binary_sensors for its state
31  // - if active, add its value to total_current_value.
32  // - creates a bitmask for the binary_sensor states on all channels
33  for (size_t i = 0; i < this->channels_.size(); i++) {
34  auto bs = this->channels_[i];
35  if (bs.binary_sensor->state) {
36  num_active_sensors++;
37  total_current_value += bs.parameters.sensor_value;
38  mask |= 1ULL << i;
39  }
40  }
41 
42  // potentially update state only if a binary_sensor is active
43  if (mask != 0ULL) {
44  // publish the average if the bitmask has changed
45  if (this->last_mask_ != mask) {
46  float publish_value = total_current_value / num_active_sensors;
47  this->publish_state(publish_value);
48  }
49  } else if (this->last_mask_ != 0ULL) {
50  // no buttons are pressed and the states have changed since last run, so publish NAN
51  ESP_LOGV(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str());
52  this->publish_state(NAN);
53  }
54 
55  this->last_mask_ = mask;
56 }
57 
59  float total_current_value = 0.0;
60  uint64_t mask = 0x00;
61 
62  // - check all binary_sensor states
63  // - if active, add its value to total_current_value
64  // - creates a bitmask for the binary_sensor states on all channels
65  for (size_t i = 0; i < this->channels_.size(); i++) {
66  auto bs = this->channels_[i];
67  if (bs.binary_sensor->state) {
68  total_current_value += bs.parameters.sensor_value;
69  mask |= 1ULL << i;
70  }
71  }
72 
73  // update state only if any binary_sensor states have changed or if no state has ever been sent on boot
74  if ((this->last_mask_ != mask) || (!this->has_state())) {
75  this->publish_state(total_current_value);
76  }
77 
78  this->last_mask_ = mask;
79 }
80 
82  float posterior_probability = this->bayesian_prior_;
83  uint64_t mask = 0x00;
84 
85  // - compute the posterior probability by taking the product of the predicate probablities for each observation
86  // - create a bitmask for the binary_sensor states on all channels/observations
87  for (size_t i = 0; i < this->channels_.size(); i++) {
88  auto bs = this->channels_[i];
89 
90  posterior_probability *=
91  this->bayesian_predicate_(bs.binary_sensor->state, posterior_probability,
92  bs.parameters.probabilities.given_true, bs.parameters.probabilities.given_false);
93 
94  mask |= ((uint64_t) (bs.binary_sensor->state)) << i;
95  }
96 
97  // update state only if any binary_sensor states have changed or if no state has ever been sent on boot
98  if ((this->last_mask_ != mask) || (!this->has_state())) {
99  this->publish_state(posterior_probability);
100  }
101 
102  this->last_mask_ = mask;
103 }
104 
105 float BinarySensorMap::bayesian_predicate_(bool sensor_state, float prior, float prob_given_true,
106  float prob_given_false) {
107  float prob_state_source_true = prob_given_true;
108  float prob_state_source_false = prob_given_false;
109 
110  // if sensor is off, then we use the probabilities for the observation's complement
111  if (!sensor_state) {
112  prob_state_source_true = 1 - prob_given_true;
113  prob_state_source_false = 1 - prob_given_false;
114  }
115 
116  return prob_state_source_true / (prior * prob_state_source_true + (1.0 - prior) * prob_state_source_false);
117 }
118 
120  BinarySensorMapChannel sensor_channel{
121  .binary_sensor = sensor,
122  .parameters{
123  .sensor_value = value,
124  },
125  };
126  this->channels_.push_back(sensor_channel);
127 }
128 
129 void BinarySensorMap::add_channel(binary_sensor::BinarySensor *sensor, float prob_given_true, float prob_given_false) {
130  BinarySensorMapChannel sensor_channel{
131  .binary_sensor = sensor,
132  .parameters{
133  .probabilities{
134  .given_true = prob_given_true,
135  .given_false = prob_given_false,
136  },
137  },
138  };
139  this->channels_.push_back(sensor_channel);
140 }
141 } // namespace binary_sensor_map
142 } // namespace esphome
void add_channel(binary_sensor::BinarySensor *sensor, float value)
Add binary_sensors to the group when only one parameter is needed for the configured mapping type...
void loop() override
The loop calls the configured type processing method.
std::vector< BinarySensorMapChannel > channels_
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
float bayesian_predicate_(bool sensor_state, float prior, float prob_given_true, float prob_given_false)
Computes the Bayesian predicate for a specific observation If the sensor state is false...
constexpr const char * c_str() const
Definition: string_ref.h:68
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
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 binary_sensor-type classes.
Definition: binary_sensor.h:37
void process_group_()
Methods to process the binary_sensor_maps types.