Inspired by custom component https://github.com/Home-Is-Where-You-Hang-Your-Hack/sensor.goveetemp_bt_hci I wanted to use these awesome sensors throughout my house but I have sections of the house I knew the BLE advertisements would not be able to reach my PC that is running home assistant so I wanted to see if it would be possible to use ESP32 bluetooth capability to read these advertisements. There’s already multiple ESPHome components that do something similar but I am a n00b at C++ and it was quite daunting trying to start writing my own component for esphome. Luckily, we dont have to do that as ESPHome has a built in feature/callback that can parse such messages with a lambda function.
Relevant ESP Home config:
esp32_ble_tracker:
on_ble_manufacturer_data_advertise:
- mac_address: A4:C1:38:11:22:33 #update to your govee device mac address
manufacturer_id: '0001'
then:
- lambda: |-
int basenum = (int16_t(x[2]) << 16) + (int16_t(x[3]) << 8) + int16_t(x[4]);
ESP_LOGD("ble_adv", "goveesensor basenum: (%d)", basenum);
bool is_negative = false;
if (basenum & 0x800000) {
is_negative = true;
basenum = basenum ^ 0x800000;
}
float temperature = basenum / 10000.0f;
float humidity = (basenum % 1000) / 10.0f;
float battery_level = uint16_t(x[5]) / 1.0f;
if (is_negative) {
temperature = -temperature;
}
ESP_LOGD("ble_adv", " Temperature: %.2f°C", temperature);
ESP_LOGD("ble_adv", " Humidity: %.2f", humidity);
ESP_LOGD("ble_adv", " Battery Level: %.0f percent", battery_level);
id(govee1_temp).publish_state(temperature);
id(govee1_humidity).publish_state(humidity);
id(govee1_battery).publish_state(battery_level);
#use this section to determine manufacturer id(mine was '0001'), can be commented out after initial discovery
on_ble_advertise:
- mac_address: A4:C1:38:11:22:33 #update to your govee device mac address
then:
- lambda: |-
ESP_LOGD("ble_adv", "New BLE device");
ESP_LOGD("ble_adv", " address: %s", x.address_str().c_str());
ESP_LOGD("ble_adv", " name: %s", x.get_name().c_str());
ESP_LOGD("ble_adv", " Advertised service UUIDs:");
for (auto uuid : x.get_service_uuids()) {
ESP_LOGD("ble_adv", " service uuid %s", uuid.to_string().c_str());
}
ESP_LOGD("ble_adv", " Advertised service data:");
for (auto data : x.get_service_datas()) {
ESP_LOGD("ble_adv", " service data uuid - %s: (length %i)", data.uuid.to_string().c_str(), data.data.size());
}
ESP_LOGD("ble_adv", " Advertised manufacturer data:");
for (auto data : x.get_manufacturer_datas()) {
ESP_LOGD("ble_adv", " mfg data uuid %s: (length %i)", data.uuid.to_string().c_str(), data.data.size());
}
sensor:
- platform: template
name: "Master Bath Temperature"
id: govee1_temp
unit_of_measurement: "°C"
accuracy_decimals: 2
- platform: template
name: "Master Bath Humidity"
id: govee1_humidity
unit_of_measurement: '%'
accuracy_decimals: 2
- platform: template
name: "Master Bath Sensor Battery Level"
id: govee1_battery
unit_of_measurement: '%'
accuracy_decimals: 0
Note(s):
I’ve confirmed this works with Govee H5101 model, which based on the lovely work by @Thrilleratplay should also work with a slight change in message format for Govee models 5102 and 5072 as well other models should be fairly straightforward to adapt, assuming they use similar structure.
Be sure to comment with your code if you find a new model working with a similar method!
I was able to do the same for the H5074. Same concept, different lambda due to the differences in the H5074 data encoding. This has been working for several weeks without issue. I use the humidity sensors to turn on the exhaust fans to run automatically to keep the humidity down when the shower is used. Working well so far.
I’m happy to see this working – I have a 5075 and I’m hoping to adjust what you all have done to make it work for it. Of course, I need to do some research to figure out what is happening here. I’d love to move this from the component and to the esp32 to be able to centralize the sensor and have more consistent readings.
Bought several H5051 sensors recently. The only update I had to make to @John.McDowell script was checking the data size was ==9 instead of ==7. Really appreciate the work put in to get this going!
This is great. Thanks all for the work you put in.
Does this work for negative temperature numbers? I have the H5075, and when the temperature goes below 0C, it get super weird numbers using the encoding described in https://github.com/Thrilleratplay/GoveeWatcher
I have H5072s (as well as H5051s). Just got it working right now. Should note is late here and I did about 20 mins of verification so far. The manufacture data you care about is EC88.
However you can use the built in function to give you back manufacture data and parse by length if you want to go that route as well. Either way will work, and using the UUID is probably more correct. Here is a working snippet you can paste in below
Complete noob questions, but does anyone know of a way to abstract this to a script that can take parameters? I have 4 of these sensors around the house and while the code isn’t that long, I’d like to have one function that can take the sensor data and a name and handle the decoding / publishing instead of having multiple copies…
relevant section above, I’ll update my first post as well. Essentially you have 6 hex chars to store data in, eg the largest number is 0xFFFFFF of which the value is encoded at the middle of min and max, or 0x800000. If you notice the value is greater than 0x800000, then the number has ‘rolled’ over to negative territory and you need to subtract of 0x800000 from the basenum and take the temp as a negtive value.
I have gotten both an H5075 and an H5101 working, each on its’ own ESP32. I would like to get multiple h5101s working on a single ESP32. I have tried a number of things but don’t seem to be able to accomplish this. Has anyone done this??
I have 9 sensors on one dedicated govee esp. I’m out now but maybe later I can post the code. It’s extremely basic and just copy and paste for each sensor.
Anyways looking at it, it is as simple as just duplicating your -mac_address: xx:xx:xx:xx:xx block inside the on_ble_adverstise block. Looking at his makes the developer in me sad as it is just copy and paste, but it has been rock solid for 2.5 months. Don’t think I will touch it tbh.
Just duplicate your code with a new mac_address for the specified ids.
Well, i finally got an m5 stack and got this working with a 5074! Amazing. When i tried to include another mac address for a model 5057 using this code it failed.
I’ve tried a couple versions of code, including removing the manufacturer Id, trying to use the if statement etc. My current version which provides the error is:
src/main.cpp:340:37: error: no match for 'operator[]' (operand types are 'const esphome::esp32_ble_tracker::ESPBTDevice' and 'int')
const int basenum = (int16_t(x[1]) << 16) + (int16_t(x[2]) << 8) + int16_t(x[3]);
^
src/main.cpp:340:61: error: no match for 'operator[]' (operand types are 'const esphome::esp32_ble_tracker::ESPBTDevice' and 'int')
const int basenum = (int16_t(x[1]) << 16) + (int16_t(x[2]) << 8) + int16_t(x[3]);
^
src/main.cpp:340:83: error: no match for 'operator[]' (operand types are 'const esphome::esp32_ble_tracker::ESPBTDevice' and 'int')
const int basenum = (int16_t(x[1]) << 16) + (int16_t(x[2]) << 8) + int16_t(x[3]);
^
src/main.cpp:344:45: error: no match for 'operator[]' (operand types are 'const esphome::esp32_ble_tracker::ESPBTDevice' and 'int')
const float battery_level = uint16_t(x[4]) / 1.0f;
^
*** [.pioenvs/govee/src/main.cpp.o] Error 1
OK – this gets me a little closer: I was looking at the python code for the govee advertisments on the original HACS plugin. It looks like 5072 and 5075 have the same config. So i used this config.> it added entities but seemed to go into a loop where the upload never finished.
OK-- maybe esp32_ble_tracker constantly loops… because when i terminate the compile after it finds the sensor it seems to update. Maybe working as designed?