Read Govee Temperature Humidity BLE Advertisements with esp32 and esphome!

It is stored in two’s complement from @Thrilleratplay script. Two’s Complement if you are interested. I’ll see if I can update the esp script this week if I have time.

1 Like

@bline79

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.

     - mac_address: XX:XX:XX:XX:XX:XX
       manufacturer_id: '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

    - mac_address: XX:XX:XX:XX:XX:XX
      then:
        - lambda: |-
            for (auto data : x.get_manufacturer_datas()) {
              if(data.data.size()==6) {
                const int basenum = (int16_t(data.data[1]) << 16) + (int16_t(data.data[2]) << 8) + int16_t(data.data[3]);
                const float temperature = (basenum / 10000.0f)*9.0/5.0 + 32.0;
                const float humidity = (basenum % 1000) / 10.0f;
                const float battery_level = uint16_t(data.data[4]) / 1.0f;
                int16_t rssi=x.get_rssi();
                id(govee_h5072_7_humidity).publish_state(humidity);
                id(govee_h5072_7_temperature).publish_state(temperature);
                id(govee_h5072_7_rssi).publish_state(rssi);
                id(govee_h5072_7_battery).publish_state(battery_level);
              } 
            }
3 Likes

@WhichWayWazzit - wanted to ping you with the same question - does this method for the H5101 work for negative numbers temperatures?

Any chance you can post the full piece of .yaml code for this? I am getting a compile error for the x.get_rssi() call.

Thanks for the work!! This worked well for me and Govee H5101 sensors.

Anyone have any ideas about how to do something similar with a remote Raspberry Pi 4b’s bluetooth?

For anyone using a Govee 5075 sensor, the following script will get you up and running:

- platform: template
    name: "Kitchen Humidity"
    id: kitchen_humidity
    unit_of_measurement: '%'
    icon: "mdi:water-percent"
  - platform: template
    name: "Kitchen Temperature"
    id: kitchen_temperature
    unit_of_measurement: '°C'
    icon: "mdi:thermometer"
  - platform: template
    name: "Kitchen Battery"
    id: kitchen_battery
    unit_of_measurement: '%'
    icon: "mdi:battery"
- mac_address: A4:C1:38:AA:BB:CC #kitchen
      manufacturer_id: EC88
      then:
        - lambda: |-
            const int basenum = (int16_t(x[1]) << 16) + (int16_t(x[2]) << 8) + int16_t(x[3]);
            ESP_LOGD("ble_adv", "goveesensor basenum: (%d)", basenum);
            const float temperature = basenum / 10000.0f;
            const float humidity = (basenum % 1000) / 10.0f;
            const float battery_level = uint16_t(x[4]) / 1.0f;
            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(kitchen_temperature).publish_state(temperature);
            id(kitchen_humidity).publish_state(humidity);
            id(kitchen_battery).publish_state(battery_level);

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…

4 Likes

Sorry for the lack of updates here. I just figured out how to do negative temps on these as well after a bit of trial/error.

esp32_ble_tracker:
  on_ble_manufacturer_data_advertise:
    - mac_address: A4:C1:38:11:22:33
      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);

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.

1 Like

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.

Thank you for quick reply. I look forward to seeing the code.

Sorry, took a bit more time than I thought.

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.

 on_ble_advertise:
    - mac_address: 1x:xx:xx:xx:xx
      then:
        - lambda: |-
            for (auto data : x.get_manufacturer_datas()) {
              if(data.data.size()==9) {
                uint16_t hum_lsb = data.data[3] + (data.data[4] << 8);
                float humidity= float(hum_lsb)/100.0;
                int16_t temp_lsb = data.data[1] + (data.data[2] << 8);
                float temperature = float(temp_lsb)/100*9.0/5.0 + 32.0;
                int16_t battery=data.data[5];
                int16_t rssi=x.get_rssi();
                id(govee_h5051_1_humidity).publish_state(humidity);
                id(govee_h5051_1_temperature).publish_state(temperature);
                id(govee_h5051_1_rssi).publish_state(rssi);
                id(govee_h5051_1_battery).publish_state(battery);
              }
            }
            
    - mac_address: 2x:xx:xx:xx:xx
      then:
        - lambda: |-
            for (auto data : x.get_manufacturer_datas()) {
              if(data.data.size()==9) {
                uint16_t hum_lsb = data.data[3] + (data.data[4] << 8);
                float humidity= float(hum_lsb)/100.0;
                int16_t temp_lsb = data.data[1] + (data.data[2] << 8);
                float temperature = float(temp_lsb)/100*9.0/5.0 + 32.0;
                int16_t battery=data.data[5];
                int16_t rssi=x.get_rssi();
                id(govee_h5051_2_humidity).publish_state(humidity);
                id(govee_h5051_2_temperature).publish_state(temperature);
                id(govee_h5051_2_rssi).publish_state(rssi);
                id(govee_h5051_2_battery).publish_state(battery);
              } 
            }

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:

esp32_ble_tracker:
  on_ble_advertise:
    #Govee 5074 -- humidor
    - mac_address: E3:60:58:E1:C0:34
      then:
        - lambda: |-
            for (auto data : x.get_manufacturer_datas()) {
                if(data.data.size()==7) {
                  uint16_t hum_lsb = data.data[3] + (data.data[4] << 8);
                  float humidity= float(hum_lsb)/100.0;
                  int16_t temp_lsb = data.data[1] + (data.data[2] << 8);
                  float temperature = float(temp_lsb)/100*9.0/5.0 + 32.0;
                  int16_t battery=data.data[5];
                  int16_t rssi=x.get_rssi();
                  id(humidor_humidity).publish_state(humidity);
                  id(humidor_temperature).publish_state(temperature);
                  id(humidor_rssi).publish_state(rssi);
                  id(humidor_battery).publish_state(battery);

                }
            }
    #Govee 5075 - office Screen
    - mac_address: A4:C1:38:37:6F:15
      then:
        - lambda: |-
            const int basenum = (int16_t(x[1]) << 16) + (int16_t(x[2]) << 8) + int16_t(x[3]);
            ESP_LOGD("ble_adv", "goveesensor basenum: (%d)", basenum);
            const float temperature = basenum / 10000.0f;
            const float humidity = (basenum % 1000) / 10.0f;
            const float battery_level = uint16_t(x[4]) / 1.0f;
            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(office_temperature).publish_state(temperature);
            id(office_humidity).publish_state(humidity);
            id(office_battery).publish_state(battery_level);

The error i get 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

Appreciate any help.

How do you know how to get this info?

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.

esp32_ble_tracker:
  on_ble_advertise:
    #Govee 5074 -- humidor
    - mac_address: E3:60:58:E1:C0:34
      then:
        - lambda: |-
            for (auto data : x.get_manufacturer_datas()) {
                if(data.data.size()==7) {
                  uint16_t hum_lsb = data.data[3] + (data.data[4] << 8);
                  float humidity= float(hum_lsb)/100.0;
                  int16_t temp_lsb = data.data[1] + (data.data[2] << 8);
                  float temperature = float(temp_lsb)/100*9.0/5.0 + 32.0;
                  int16_t battery=data.data[5];
                  int16_t rssi=x.get_rssi();
                  id(humidor_humidity).publish_state(humidity);
                  id(humidor_temperature).publish_state(temperature);
                  id(humidor_rssi).publish_state(rssi);
                  id(humidor_battery).publish_state(battery);

                }
            }
    #Govee 5075 - office Screen
    - mac_address: A4:C1:38:37:6F:15
      then:
        - lambda: |-
            for (auto data : x.get_manufacturer_datas()) {
              if(data.data.size()==6) {
                const int basenum = (int16_t(data.data[1]) << 16) + (int16_t(data.data[2]) << 8) + int16_t(data.data[3]);
                const float temperature = (basenum / 10000.0f)*9.0/5.0 + 32.0;
                const float humidity = (basenum % 1000) / 10.0f;
                const float battery_level = uint16_t(data.data[4]) / 1.0f;
                int16_t rssi=x.get_rssi();
                id(office_humidity).publish_state(humidity);
                id(office_temperature).publish_state(temperature);
                id(office_rssi).publish_state(rssi);
                id(office_battery).publish_state(battery_level);
              } 
            }
1 Like

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?

I have a c language program that has the decodes for the following units, code is a bit ragged but perhaps it will give you some ideas:

//  1 = Xiaomi LYWSD03MMC-ATC   https://github.com/atc1441/ATC_MiThermometer
//  2 = Govee H5052 (type 4 advertising packets)
//  3 = Govee H5072
//  4 = Govee H5102
//  5 = Govee H5075
//  6 = Govee H5074 (type 4 advertising packets)
// 99 = Display raw type 0 and type 4 advertising packets for this BLE MAC address

Has anyone tried this integration with a B5178? It is a dual indoor and outdoor thermometer and humidity sensor. It uses Bluetooth so I would think it would be similar but the packets might be a little different. Here is a link https://www.amazon.com/gp/product/B0872ZPQSJ to the one I purchased. It communicates over 433 between the base unit and the outdoor sensor but the communication to the phone is BLE.

For thirty bucks you can get 10 Xiaomi LYWSD03MMC units that you can have working in 30 minutes. Or 5 of them and a six pack of IPA s :wink: Only benny I can see of this unit is the 433 mhz, if you needed to have a sensor a long distance away. Assuming that the range of this 433 mhz device is good and better than you could get with a BLE device.

The reason I liked the B5178 is because it has an outdoor module that has no screen. It is just a module with a battery. Do you know how the Xiaomi LYWSD03MMC does outdoors?

I read through some of the Q&A section for the product on Amazon, you might want to do the same… vendors response on using it outside : ‘Please protect it from the weather.’ :wink:

My Xiaomi has been outside 7x24 for six months inside two ziplock sandwich bags. If you want to go 1st class, get one of the mobile phone waterproof bags for a couple bucks.

Rain, hail wind and temps from 28F to 140F recorded so far (it is not completely shaded from sun). The biggest issue with all of these outdoor sensors is there humidity readings after rain. You end up with water all around the sensor until the sun evaporates it, so you end up with inaccurate humidity reading for a considerable period after rain or other significant moisture.

Battery at 64% after six months:

'Is the outdoor sensor waterproof? I want to place it outside in my garden area. Do I need to protect it from the weather in some way?
Answer:The outdoor sensor is not totally waterproof. Please protect it from the weather.
By Govee US SELLER  on March 30, 2021'

OK. Thanks. I ordered some Xiaomi LYWSD03MMC as I found a outdoor wall mount for it on thingiverse that I can print. I bought a backup one in case it gets fried. I will take into account the humidity delay after a rain.

With the ziplocs how do you handle letting air in so it can measure humidity?