ESPHome 2026.1.5
Loading...
Searching...
No Matches
mopeka_pro_check.cpp
Go to the documentation of this file.
1#include "mopeka_pro_check.h"
2#include "esphome/core/log.h"
3
4#ifdef USE_ESP32
5
6namespace esphome {
7namespace mopeka_pro_check {
8
9static const char *const TAG = "mopeka_pro_check";
10static const uint8_t MANUFACTURER_DATA_LENGTH = 10;
11static const uint16_t MANUFACTURER_ID = 0x0059;
12static const double MOPEKA_LPG_COEF[] = {0.573045, -0.002822, -0.00000535}; // Magic numbers provided by Mopeka
13
15 ESP_LOGCONFIG(TAG, "Mopeka Pro Check");
16 LOG_SENSOR(" ", "Level", this->level_);
17 LOG_SENSOR(" ", "Temperature", this->temperature_);
18 LOG_SENSOR(" ", "Battery Level", this->battery_level_);
19 LOG_SENSOR(" ", "Reading Distance", this->distance_);
20 LOG_SENSOR(" ", "Read Quality", this->read_quality_);
21 LOG_SENSOR(" ", "Ignored Reads", this->ignored_reads_);
22}
23
30 if (device.address_uint64() != this->address_) {
31 return false;
32 }
33
34 char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
35 ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str_to(addr_buf));
36
37 const auto &manu_datas = device.get_manufacturer_datas();
38
39 if (manu_datas.size() != 1) {
40 ESP_LOGE(TAG, "Unexpected manu_datas size (%d)", manu_datas.size());
41 return false;
42 }
43
44 const auto &manu_data = manu_datas[0];
45
46 ESP_LOGVV(TAG, "Manufacturer data:");
47 for (const uint8_t byte : manu_data.data) {
48 ESP_LOGVV(TAG, "0x%02x", byte);
49 }
50
51 if (manu_data.data.size() != MANUFACTURER_DATA_LENGTH) {
52 ESP_LOGE(TAG, "Unexpected manu_data size (%d)", manu_data.data.size());
53 return false;
54 }
55
56 // Now parse the data - See Datasheet for definition
57
58 if (static_cast<SensorType>(manu_data.data[0]) != STANDARD_BOTTOM_UP &&
59 static_cast<SensorType>(manu_data.data[0]) != LIPPERT_BOTTOM_UP &&
60 static_cast<SensorType>(manu_data.data[0]) != PLUS_BOTTOM_UP &&
61 static_cast<SensorType>(manu_data.data[0]) != PRO_UNIVERSAL) {
62 ESP_LOGE(TAG, "Unsupported Sensor Type (0x%X)", manu_data.data[0]);
63 return false;
64 }
65
66 // Get battery level first
67 if (this->battery_level_ != nullptr) {
68 uint8_t level = this->parse_battery_level_(manu_data.data);
69 this->battery_level_->publish_state(level);
70 }
71
72 // Get the quality value
73 SensorReadQuality quality_value = this->parse_read_quality_(manu_data.data);
74 if (this->read_quality_ != nullptr) {
75 this->read_quality_->publish_state(static_cast<int>(quality_value));
76 }
77
78 // Determine if we have a good enough quality of read to report level and distance
79 // sensors. This sensor is reported regardless of distance or level sensors being enabled
80 if (quality_value < this->min_signal_quality_) {
81 ESP_LOGW(TAG, "Read Quality too low to report distance or level");
82 this->ignored_read_count_++;
83 } else {
84 // reset to zero since read quality was sufficient
85 this->ignored_read_count_ = 0;
86 }
87 // Report number of contiguous ignored reads if sensor defined
88 if (this->ignored_reads_ != nullptr) {
90 }
91
92 // Get distance and level if either are sensors
93 if ((this->distance_ != nullptr) || (this->level_ != nullptr)) {
94 uint32_t distance_value = this->parse_distance_(manu_data.data);
95 ESP_LOGD(TAG, "Distance Sensor: Quality (0x%X) Distance (%" PRId32 "mm)", quality_value, distance_value);
96
97 // only update distance and level sensors if read quality was sufficient. This can be determined by
98 // if the ignored_read_count is zero.
99 if (this->ignored_read_count_ == 0) {
100 // update distance sensor
101 if (this->distance_ != nullptr) {
102 this->distance_->publish_state(distance_value);
103 }
104
105 // update level sensor
106 if (this->level_ != nullptr) {
107 uint8_t tank_level = 0;
108 if (distance_value >= this->full_mm_) {
109 tank_level = 100; // cap at 100%
110 } else if (distance_value > this->empty_mm_) {
111 tank_level = ((100.0f / (this->full_mm_ - this->empty_mm_)) * (distance_value - this->empty_mm_));
112 }
113 this->level_->publish_state(tank_level);
114 }
115 }
116 }
117
118 // Get temperature of sensor
119 if (this->temperature_ != nullptr) {
120 int8_t temp_in_c = this->parse_temperature_(manu_data.data);
121 this->temperature_->publish_state(temp_in_c);
122 }
123
124 return true;
125}
126
127uint8_t MopekaProCheck::parse_battery_level_(const std::vector<uint8_t> &message) {
128 float v = (float) ((message[1] & 0x7F) / 32.0f);
129 // convert voltage and scale for CR2032
130 float percent = (v - 2.2f) / 0.65f * 100.0f;
131 if (percent < 0.0f) {
132 return 0;
133 }
134 if (percent > 100.0f) {
135 return 100;
136 }
137 return (uint8_t) percent;
138}
139
140uint32_t MopekaProCheck::parse_distance_(const std::vector<uint8_t> &message) {
141 uint16_t raw = (message[4] * 256) + message[3];
142 double raw_level = raw & 0x3FFF;
143 double raw_t = (message[2] & 0x7F);
144
145 return (uint32_t) (raw_level *
146 (MOPEKA_LPG_COEF[0] + MOPEKA_LPG_COEF[1] * raw_t + MOPEKA_LPG_COEF[2] * raw_t * raw_t));
147}
148
149int8_t MopekaProCheck::parse_temperature_(const std::vector<uint8_t> &message) { return (message[2] & 0x7F) - 40; }
150
152 // Since a 8 bit value is being shifted and truncated to 2 bits all possible values are defined as enumeration
153 // value and the static cast is safe.
154 return static_cast<SensorReadQuality>(message[4] >> 6);
155}
156
157} // namespace mopeka_pro_check
158} // namespace esphome
159
160#endif
uint8_t raw[35]
Definition bl0939.h:0
const char * address_str_to(std::span< char, MAC_ADDRESS_PRETTY_BUFFER_SIZE > buf) const
Format MAC address into provided buffer, returns pointer to buffer for convenience.
const std::vector< ServiceData > & get_manufacturer_datas() const
SensorReadQuality parse_read_quality_(const std::vector< uint8_t > &message)
int8_t parse_temperature_(const std::vector< uint8_t > &message)
uint8_t parse_battery_level_(const std::vector< uint8_t > &message)
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override
Main parse function that gets called for all ble advertisements.
uint32_t parse_distance_(const std::vector< uint8_t > &message)
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:76
const char * message
Definition component.cpp:38
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7