ESPHome 2026.1.2
Loading...
Searching...
No Matches
xiaomi_miscale.cpp
Go to the documentation of this file.
1#include "xiaomi_miscale.h"
3#include "esphome/core/log.h"
4
5#ifdef USE_ESP32
6
7namespace esphome {
8namespace xiaomi_miscale {
9
10static const char *const TAG = "xiaomi_miscale";
11
13 ESP_LOGCONFIG(TAG, "Xiaomi Miscale");
14 LOG_SENSOR(" ", "Weight", this->weight_);
15 LOG_SENSOR(" ", "Impedance", this->impedance_);
16}
17
19 if (device.address_uint64() != this->address_) {
20 ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
21 return false;
22 }
23 char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
24 const char *addr_str = device.address_str_to(addr_buf);
25 ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", addr_str);
26
27 bool success = false;
28 for (auto &service_data : device.get_service_datas()) {
29 auto res = parse_header_(service_data);
30 if (!res.has_value())
31 continue;
32
33 if (!parse_message_(service_data.data, *res))
34 continue;
35
36 if (!report_results_(res, addr_str))
37 continue;
38
39 if (res->weight.has_value() && this->weight_ != nullptr)
40 this->weight_->publish_state(*res->weight);
41
42 if (this->impedance_ != nullptr) {
43 if (res->version == 1) {
44 ESP_LOGW(TAG, "Impedance is only supported on version 2. Your scale was identified as version 1.");
45 } else {
46 if (res->impedance.has_value()) {
47 this->impedance_->publish_state(*res->impedance);
48 } else {
50 this->impedance_->publish_state(NAN);
51 }
52 }
53 }
54 success = true;
55 }
56
57 return success;
58}
59
61 ParseResult result;
62 if (service_data.uuid == esp32_ble_tracker::ESPBTUUID::from_uint16(0x181D) && service_data.data.size() == 10) {
63 result.version = 1;
64 } else if (service_data.uuid == esp32_ble_tracker::ESPBTUUID::from_uint16(0x181B) && service_data.data.size() == 13) {
65 result.version = 2;
66 } else {
67 char uuid_buf[esp32_ble::UUID_STR_LEN];
68 ESP_LOGVV(TAG,
69 "parse_header(): Couldn't identify scale version or data size was not correct. UUID: %s, data_size: %d",
70 service_data.uuid.to_str(uuid_buf), service_data.data.size());
71 return {};
72 }
73
74 return result;
75}
76
77bool XiaomiMiscale::parse_message_(const std::vector<uint8_t> &message, ParseResult &result) {
78 if (result.version == 1) {
79 return parse_message_v1_(message, result);
80 } else {
81 return parse_message_v2_(message, result);
82 }
83}
84
85bool XiaomiMiscale::parse_message_v1_(const std::vector<uint8_t> &message, ParseResult &result) {
86 // message size is checked in parse_header
87 // 1-2 Weight (MISCALE 181D)
88 // 3-4 Years (MISCALE 181D)
89 // 5 month (MISCALE 181D)
90 // 6 day (MISCALE 181D)
91 // 7 hour (MISCALE 181D)
92 // 8 minute (MISCALE 181D)
93 // 9 second (MISCALE 181D)
94
95 const uint8_t *data = message.data();
96
97 // weight, 2 bytes, 16-bit unsigned integer, 1 kg
98 const int16_t weight = uint16_t(data[1]) | (uint16_t(data[2]) << 8);
99 if (data[0] == 0x22 || data[0] == 0xa2) {
100 result.weight = weight * 0.01f / 2.0f; // unit 'kg'
101 } else if (data[0] == 0x12 || data[0] == 0xb2) {
102 result.weight = weight * 0.01f * 0.6f; // unit 'jin'
103 } else if (data[0] == 0x03 || data[0] == 0xb3) {
104 result.weight = weight * 0.01f * 0.453592f; // unit 'lbs'
105 }
106
107 return true;
108}
109
110bool XiaomiMiscale::parse_message_v2_(const std::vector<uint8_t> &message, ParseResult &result) {
111 // message size is checked in parse_header
112 // 2-3 Years (MISCALE 2 181B)
113 // 4 month (MISCALE 2 181B)
114 // 5 day (MISCALE 2 181B)
115 // 6 hour (MISCALE 2 181B)
116 // 7 minute (MISCALE 2 181B)
117 // 8 second (MISCALE 2 181B)
118 // 9-10 impedance (MISCALE 2 181B)
119 // 11-12 weight (MISCALE 2 181B)
120
121 const uint8_t *data = message.data();
122
123 bool has_impedance = ((data[1] & (1 << 1)) != 0);
124 bool is_stabilized = ((data[1] & (1 << 5)) != 0);
125 bool load_removed = ((data[1] & (1 << 7)) != 0);
126
127 if (!is_stabilized || load_removed) {
128 return false;
129 }
130
131 // weight, 2 bytes, 16-bit unsigned integer, 1 kg
132 const int16_t weight = uint16_t(data[11]) | (uint16_t(data[12]) << 8);
133 if (data[0] == 0x02) {
134 result.weight = weight * 0.01f / 2.0f; // unit 'kg'
135 } else if (data[0] == 0x03) {
136 result.weight = weight * 0.01f * 0.453592f; // unit 'lbs'
137 }
138
139 if (has_impedance) {
140 // impedance, 2 bytes, 16-bit
141 const int16_t impedance = uint16_t(data[9]) | (uint16_t(data[10]) << 8);
142 result.impedance = impedance;
143
144 if (impedance == 0 || impedance >= 3000) {
145 return false;
146 }
147 }
148
149 return true;
150}
151
153 if (!result.has_value()) {
154 ESP_LOGVV(TAG, "report_results(): no results available.");
155 return false;
156 }
157
158 ESP_LOGD(TAG, "Got Xiaomi Miscale v%d (%s):", result->version, address);
159
160 if (result->weight.has_value()) {
161 ESP_LOGD(TAG, " Weight: %.2fkg", *result->weight);
162 }
163 if (result->impedance.has_value()) {
164 ESP_LOGD(TAG, " Impedance: %.0fohm", *result->impedance);
165 }
166
167 return true;
168}
169
170} // namespace xiaomi_miscale
171} // namespace esphome
172
173#endif
uint8_t address
Definition bl0906.h:4
const char * to_str(std::span< char, UUID_STR_LEN > output) const
Definition ble_uuid.cpp:146
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_service_datas() const
bool has_value() const
Definition optional.h:92
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:76
bool parse_message_v1_(const std::vector< uint8_t > &message, ParseResult &result)
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override
bool parse_message_(const std::vector< uint8_t > &message, ParseResult &result)
optional< ParseResult > parse_header_(const esp32_ble_tracker::ServiceData &service_data)
bool report_results_(const optional< ParseResult > &result, const char *address)
bool parse_message_v2_(const std::vector< uint8_t > &message, ParseResult &result)
const char * message
Definition component.cpp:38
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7