ESPHome  2024.4.0
atc_mithermometer.cpp
Go to the documentation of this file.
1 #include "atc_mithermometer.h"
2 #include "esphome/core/log.h"
3 
4 #ifdef USE_ESP32
5 
6 namespace esphome {
7 namespace atc_mithermometer {
8 
9 static const char *const TAG = "atc_mithermometer";
10 
12  ESP_LOGCONFIG(TAG, "ATC MiThermometer");
13  LOG_SENSOR(" ", "Temperature", this->temperature_);
14  LOG_SENSOR(" ", "Humidity", this->humidity_);
15  LOG_SENSOR(" ", "Battery Level", this->battery_level_);
16  LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_);
17 }
18 
20  if (device.address_uint64() != this->address_) {
21  ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
22  return false;
23  }
24  ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str());
25 
26  bool success = false;
27  for (auto &service_data : device.get_service_datas()) {
28  auto res = parse_header_(service_data);
29  if (!res.has_value()) {
30  continue;
31  }
32  if (!(parse_message_(service_data.data, *res))) {
33  continue;
34  }
35  if (!(report_results_(res, device.address_str()))) {
36  continue;
37  }
38  if (res->temperature.has_value() && this->temperature_ != nullptr)
39  this->temperature_->publish_state(*res->temperature);
40  if (res->humidity.has_value() && this->humidity_ != nullptr)
41  this->humidity_->publish_state(*res->humidity);
42  if (res->battery_level.has_value() && this->battery_level_ != nullptr)
43  this->battery_level_->publish_state(*res->battery_level);
44  if (res->battery_voltage.has_value() && this->battery_voltage_ != nullptr)
45  this->battery_voltage_->publish_state(*res->battery_voltage);
46  success = true;
47  }
48  if (this->signal_strength_ != nullptr)
49  this->signal_strength_->publish_state(device.get_rssi());
50 
51  return success;
52 }
53 
55  ParseResult result;
56  if (!service_data.uuid.contains(0x1A, 0x18)) {
57  ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes.");
58  return {};
59  }
60 
61  auto raw = service_data.data;
62 
63  static uint8_t last_frame_count = 0;
64  if (last_frame_count == raw[12]) {
65  ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%hhu).", last_frame_count);
66  return {};
67  }
68  last_frame_count = raw[12];
69 
70  return result;
71 }
72 
73 bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, ParseResult &result) {
74  // Byte 0-5 mac in correct order
75  // Byte 6-7 Temperature in uint16
76  // Byte 8 Humidity in percent
77  // Byte 9 Battery in percent
78  // Byte 10-11 Battery in mV uint16_t
79  // Byte 12 frame packet counter
80 
81  const uint8_t *data = message.data();
82  const int data_length = 13;
83 
84  if (message.size() != data_length) {
85  ESP_LOGVV(TAG, "parse_message(): payload has wrong size (%d)!", message.size());
86  return false;
87  }
88 
89  // temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
90  const int16_t temperature = uint16_t(data[7]) | (uint16_t(data[6]) << 8);
91  result.temperature = temperature / 10.0f;
92 
93  // humidity, 1 byte, 8-bit unsigned integer, 1.0 %
94  result.humidity = data[8];
95 
96  // battery, 1 byte, 8-bit unsigned integer, 1.0 %
97  result.battery_level = data[9];
98 
99  // battery, 2 bytes, 16-bit unsigned integer, 0.001 V
100  const int16_t battery_voltage = uint16_t(data[11]) | (uint16_t(data[10]) << 8);
101  result.battery_voltage = battery_voltage / 1.0e3f;
102 
103  return true;
104 }
105 
106 bool ATCMiThermometer::report_results_(const optional<ParseResult> &result, const std::string &address) {
107  if (!result.has_value()) {
108  ESP_LOGVV(TAG, "report_results(): no results available.");
109  return false;
110  }
111 
112  ESP_LOGD(TAG, "Got ATC MiThermometer (%s):", address.c_str());
113 
114  if (result->temperature.has_value()) {
115  ESP_LOGD(TAG, " Temperature: %.1f °C", *result->temperature);
116  }
117  if (result->humidity.has_value()) {
118  ESP_LOGD(TAG, " Humidity: %.0f %%", *result->humidity);
119  }
120  if (result->battery_level.has_value()) {
121  ESP_LOGD(TAG, " Battery Level: %.0f %%", *result->battery_level);
122  }
123  if (result->battery_voltage.has_value()) {
124  ESP_LOGD(TAG, " Battery Voltage: %.3f V", *result->battery_voltage);
125  }
126 
127  return true;
128 }
129 
130 } // namespace atc_mithermometer
131 } // namespace esphome
132 
133 #endif
uint8_t raw[35]
Definition: bl0939.h:19
optional< ParseResult > parse_header_(const esp32_ble_tracker::ServiceData &service_data)
bool report_results_(const optional< ParseResult > &result, const std::string &address)
bool has_value() const
Definition: optional.h:87
bool parse_message_(const std::vector< uint8_t > &message, ParseResult &result)
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
uint16_t temperature
Definition: sun_gtil2.cpp:26
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override
const std::vector< ServiceData > & get_service_datas() const
bool contains(uint8_t data1, uint8_t data2) const
Definition: ble_uuid.cpp:119
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7