├── images
├── init.txt
├── battery.jpg
├── chest_freezer_test.jpg
├── chest_freezer_temp_sensor.jpg
├── chest_freezer-cooldown_warmup.jpg
└── energy_consumption_monitoring.jpg
├── README.md
└── govee_ble_mqtt_pi.py
/images/init.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/images/battery.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsaitsai/govee_bluetooth_gateway/HEAD/images/battery.jpg
--------------------------------------------------------------------------------
/images/chest_freezer_test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsaitsai/govee_bluetooth_gateway/HEAD/images/chest_freezer_test.jpg
--------------------------------------------------------------------------------
/images/chest_freezer_temp_sensor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsaitsai/govee_bluetooth_gateway/HEAD/images/chest_freezer_temp_sensor.jpg
--------------------------------------------------------------------------------
/images/chest_freezer-cooldown_warmup.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsaitsai/govee_bluetooth_gateway/HEAD/images/chest_freezer-cooldown_warmup.jpg
--------------------------------------------------------------------------------
/images/energy_consumption_monitoring.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsaitsai/govee_bluetooth_gateway/HEAD/images/energy_consumption_monitoring.jpg
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # govee_bluetooth_gateway
2 | **Bluetooth to MQTT gateway for Govee brand bluetooth sensors.**
3 |
4 |
5 |
6 | I got a chest freezer recently, and planned to put it in the basement. Since it's not used frequently, I wanted to keep track of the temperature and send myself a notification in case the freezer breaks down and I need to save the food.
7 |
8 | I have zigbee temperature sensors that are small and use CR2032 coin cells. They'll work for a little while, but freezing temperatures may not be best for coin cell battery performance and might necessitate frequent battery changes. I came across these [Govee brand](https://www.amazon.com/Govee-Temperature-Notification-Hygrometer-Thermometer/dp/B0872X4H4J) Bluetooth temperature sensors that use a pair of AAA batteries.
9 |
10 | If I swap the alkaline AAA batteries out with Energizer Lithium AAA batteries, the sensor should work better in a chest freezer. The lithium-iron disulfide chemistry in these Energizer AAA batteries perform better in freezing conditions than the Lithium Manganese dioxide chemistry used in typical CR2032 coin cells. The added capacity should also reduce battery changes.
11 |
12 |
13 |
14 |
15 | Like a lot of these gateway projects, I wanted to use a Raspberry Pi to read the bluetooth sensor data and act as a dedicated gateway. I came across this [GoveeWatcher project](https://github.com/Thrilleratplay/GoveeWatcher/issues/2) that explains the how the Govee bluetooth advertisement formats the temperature, humidity, and battery level. The sensor data is encoded in the advertisement, so no GATT connections needed. The manufacturer provides mobile apps for typical usage, but it has creepy permissions and it's not useful for my purpose. Instead, I used the Bluepy library to read the BLE advertisement and parse it out per the description from GoveeWatcher project. As I'm already using MQTT, Influx, Grafana, and telegraph, it was convenient to publish the sensor data as MQTT messages and visualize it in Grafana. Then send email notifications using Node-Red.
16 |
17 |
18 |
19 | I'm also interested in how the freezer behaves. How the temperature fluctuates, how many watts it consumes, etc. It's interesting how long it takes for it to go from -13F back put up to 32F when the power is shut off. To monitor energy consumption, I used a [wifi outlet](https://www.amazon.com/BN-LINK-Monitoring-Function-Compatible-Assistant/dp/B07VDGM6QR) with built-in current sensor and flashed it with Tasmota via [Tuya Convert](https://github.com/ct-Open-Source/tuya-convert). This 7 cu-ft Hisense chest freezer uses about 0.7 kWh per day when the unit is totally empty and door kept closed.
20 |
21 |
22 |
23 | I'll update the energy usage once the freezer is more full of food. I'll also update when I get battery life data. The Govee bluetooth sensor unfortunately sends out updates way more often than necessary (every couple of seconds). So the added battery capacity from the AAA batteries may be somewhat negated by the excess data transmissions.
24 |
25 | The chest freezer isn't blocking the signal too much. The Pi gateway is one level up and one room over from the chest freezer location, and I'm able to receive 3 out of 5 advertisements on average.
26 |
--------------------------------------------------------------------------------
/govee_ble_mqtt_pi.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | '''
3 | This is a python Bluetooth advertisement scanner for the Govee brand Bluetooth
4 | temperature sensor. Tested on model H5075 using Raspberry Pi 3.
5 | Temperature, humidity, and battery level is published as MQTT messages.
6 |
7 | Credit: I used information for Govee advertisement format from
8 | github.com/Thrilleratplay/GoveeWatcher
9 |
10 | Install dependencies:
11 | sudo apt-get install python3-pip libglib2.0-dev
12 | sudo pip3 install bluepy
13 | sudo apt install -y mosquitto mosquitto-clients
14 | sudo pip3 install paho-mqtt
15 |
16 | Needs sudo to run on Raspbian
17 | sudo python3 govee_ble_mqtt_pi.py
18 |
19 | Run in background
20 | sudo nohup python3 govee_ble_mqtt_pi.py &
21 |
22 | '''
23 |
24 | from __future__ import print_function
25 |
26 | from time import gmtime, strftime, sleep
27 | from bluepy.btle import Scanner, DefaultDelegate, BTLEException
28 | import sys
29 | import paho.mqtt.client as mqtt
30 |
31 | def on_connect(client, userdata, flags, rc):
32 | print("Connected with result code "+str(rc))
33 |
34 | def on_message(client, userdata, msg):
35 | print("on message")
36 |
37 | client = mqtt.Client()
38 | mqtt_prefix = "/sensor/govee"
39 | mqtt_gateway_name = "/upstairs/"
40 |
41 | class ScanDelegate(DefaultDelegate):
42 |
43 | global client
44 | # mqtt message topic/payload: /prefix/gateway_name/mac/
45 | global mqtt_prefix
46 | global mqtt_gateway_name
47 |
48 | def handleDiscovery(self, dev, isNewDev, isNewData):
49 | #if (dev.addr == "a4:c1:38:xx:xx:xx") or (dev.addr == "a4:c1:38:xx:xx:xx"):
50 | if dev.addr[:8]=="a4:c1:38":
51 |
52 | #returns a list, of which the [2] item of the [3] tupple is manufacturing data
53 | adv_list = dev.getScanData()
54 |
55 | adv_manuf_data = adv_list[2][2]
56 |
57 | #print("manuf data = ", adv_manuf_data)
58 |
59 | #this is the location of the encoded temp/humidity and battery data
60 | temp_hum_data = adv_manuf_data[6:12]
61 | battery = adv_manuf_data[12:14]
62 |
63 | #convert to integer
64 | val = (int(temp_hum_data, 16))
65 |
66 | #decode tip from eharris: https://github.com/Thrilleratplay/GoveeWatcher/issues/2
67 | is_negative = False
68 | temp_C = 500
69 | humidity = 500
70 | if (val & 0x800000):
71 | is_negative = True
72 | val = val ^ 0x800000
73 | try:
74 | humidity = (val % 1000) / 10
75 | temp_C = int(val / 1000) / 10
76 | if (is_negative):
77 | temp_C = 0 - temp_C
78 | except:
79 | print("issues with integer conversion")
80 |
81 | try:
82 | battery_percent = int(adv_manuf_data[12:14]) / 64 * 100
83 | except:
84 | battery_percent = 200
85 | battery_percent = round(battery_percent)
86 |
87 | temp_F = round(temp_C*9/5+32, 1)
88 |
89 | try:
90 | hum_percent = ((int(temp_hum_data, 16)) % 1000) / 10
91 | except:
92 | hum_percent = 200
93 | hum_percent = round(hum_percent)
94 | mac=dev.addr
95 | signal = dev.rssi
96 |
97 | #print("mac=", mac, " percent humidity ", hum_percent, " temp_F = ", temp_F, " battery percent=", battery_percent, " rssi=", signal)
98 | mqtt_topic = mqtt_prefix + mqtt_gateway_name + mac + "/"
99 |
100 | client.publish(mqtt_topic+"rssi", signal, qos=0)
101 | client.publish(mqtt_topic+"temp_F", temp_F, qos=0)
102 | client.publish(mqtt_topic+"hum", hum_percent, qos=0)
103 | client.publish(mqtt_topic+"battery_pct", battery_percent, qos=0)
104 |
105 | sys.stdout.flush()
106 |
107 | scanner = Scanner().withDelegate(ScanDelegate())
108 |
109 | #replace localhost with your MQTT broker
110 | client.connect("localhost",1883,60)
111 |
112 | client.on_connect = on_connect
113 | client.on_message = on_message
114 |
115 | while True:
116 | scanner.scan(60.0, passive=True)
117 |
118 |
--------------------------------------------------------------------------------