Skip to content

Add Govee H5106 #1189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions custom_components/ble_monitor/ble_parser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,8 @@ def parse_advertisement(
data_len = man_spec_data[0]
# Filter on Company Identifier
if comp_id == 0x0001 and data_len in [0x09, 0x0C, 0x22, 0x25]:
# Govee H5101/H5102/H5177
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, mac, rssi)
# Govee H5101/H5102/H5106/H5177
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, local_name, mac, rssi)
break
elif comp_id == 0x004C and man_spec_data[4] == 0x02:
# iBeacon
Expand Down Expand Up @@ -354,34 +354,34 @@ def parse_advertisement(
break
elif comp_id == 0x2730 and data_len in [0x14, 0x2D]:
# Govee H5182
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, mac, rssi)
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, local_name, mac, rssi)
break
elif comp_id == 0x1B36 and data_len in [0x14, 0x2D]:
# Govee H5184
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, mac, rssi)
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, local_name, mac, rssi)
break
elif comp_id in [0x4A32, 0x332, 0x4C32] and data_len in [0x17, 0x2D]:
# Govee H5185
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, mac, rssi)
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, local_name, mac, rssi)
break
elif comp_id in [0x67DD, 0xE02F, 0xF79F] and data_len in [0x11, 0x2A]:
# Govee H5183
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, mac, rssi)
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, local_name, mac, rssi)
elif comp_id in [0x5112, 0x5122, 0x6111, 0x6121] and data_len == 0x0f:
# Air Mentor 2S
sensor_data = parse_airmentor(self, man_spec_data, mac, rssi)
break
elif comp_id == 0x8801 and data_len in [0x0C, 0x25]:
# Govee H5179
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, mac, rssi)
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, local_name, mac, rssi)
break
elif comp_id == 0xAA55 and data_len == 0x14:
# Thermobeacon
sensor_data = parse_thermobeacon(self, man_spec_data, mac, rssi)
break
elif comp_id == 0xEC88 and data_len in [0x09, 0x0A, 0x0C, 0x22, 0x24, 0x25]:
# Govee H5051/H5071/H5072/H5075/H5074
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, mac, rssi)
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, local_name, mac, rssi)
break
elif comp_id == 0xFFFF and data_len == 0x1E:
# Kegtron
Expand Down Expand Up @@ -415,15 +415,15 @@ def parse_advertisement(
break
elif service_class_uuid16 in [0x5182, 0x5184] and data_len in [0x14, 0x2D]:
# Govee H5182 and H5184
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, mac, rssi)
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, local_name, mac, rssi)
break
elif service_class_uuid16 == 0x5183 and data_len in [0x11, 0x2A]:
# Govee H5183
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, mac, rssi)
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, local_name, mac, rssi)
break
elif service_class_uuid16 in [0x5185, 0x5198] and data_len in [0x17, 0x30]:
# Govee H5185 and H5198
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, mac, rssi)
sensor_data = parse_govee(self, man_spec_data, service_class_uuid16, local_name, mac, rssi)
break
elif service_class_uuid16 == 0xF0FF:
if comp_id in [0x0010, 0x0011, 0x0015, 0x0018, 0x001B] and data_len in [0x15, 0x17]:
Expand Down
33 changes: 32 additions & 1 deletion custom_components/ble_monitor/ble_parser/govee.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,41 @@ def decode_temps(packet_value: int) -> float:
return float(int(packet_value / 1000) / 10)


def decode_temps_from_4_bytes(packet_value: int) -> float:
"""Decode temperature values (to one decimal place)."""
if packet_value & 0x80000000:
# Handle freezing temperatures
packet_value &= 0x7FFFFFFF
return float(int(packet_value / -10000000) / -10)
return float(int(packet_value / 1000000) / 10)


def decode_humi(packet_value: int) -> float:
"""Decode humidity values (to one decimal place)"""
packet_value &= 0x7FFFFF
return float((packet_value % 1000) / 10)


def decode_humi_from_4_bytes(packet_value: int) -> float:
"""Decode humidity values (to one decimal place)"""
packet_value &= 0x7FFFFFFF
return float(int((packet_value % 1000000) / 1000) / 10)


def decode_temps_probes(packet_value: int) -> float:
"""Filter potential negative temperatures."""
if packet_value < 0:
return 0.0
return float(packet_value / 100)


def parse_govee(self, data, service_class_uuid16, source_mac, rssi):
def decode_pm25_from_4_bytes(packet_value: int) -> int:
"""Decode humidity values"""
packet_value &= 0x7FFFFFFF
return int(packet_value % 1000)


def parse_govee(self, data, service_class_uuid16, local_name, source_mac, rssi):
"""Parser for Govee sensors"""
# The parser needs to handle the bug in the Govee BLE advertisement
# data as INTELLI_ROCKS sometimes ends up glued on to the end of the message
Expand All @@ -50,6 +71,16 @@ def parse_govee(self, data, service_class_uuid16, source_mac, rssi):
humi = decode_humi(packet)
batt = int(data[8])
result.update({"temperature": temp, "humidity": humi, "battery": batt})
elif msg_length == 10 and (
service_class_uuid16 == 0x5106 or (device_id == 0x0001 and local_name.startswith("GVH5106"))
):
device_type = "H5106"
packet_5106 = data[6:10].hex()
packet = int(packet_5106, 16)
temp = decode_temps_from_4_bytes(packet)
humi = decode_humi_from_4_bytes(packet)
pm25 = decode_pm25_from_4_bytes(packet)
result.update({"temperature": temp, "humidity": humi, "pm2.5": pm25})
elif msg_length == 10 and (
service_class_uuid16 == 0x5101
or service_class_uuid16 == 0x5102
Expand Down
2 changes: 2 additions & 0 deletions custom_components/ble_monitor/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,7 @@ class BLEMonitorBinarySensorEntityDescription(
'H5052' : [["temperature", "humidity", "battery", "rssi"], [], []],
'H5071' : [["temperature", "humidity", "battery", "rssi"], [], []],
'H5074' : [["temperature", "humidity", "battery", "rssi"], [], []],
'H5106' : [["temperature", "humidity", "pm2.5", "rssi"], [], []],
'H5178' : [["temperature", "humidity", "battery", "rssi"], [], []],
'H5178-outdoor' : [["temperature", "humidity", "battery", "rssi"], [], []],
'H5179' : [["temperature", "humidity", "battery", "rssi"], [], []],
Expand Down Expand Up @@ -1366,6 +1367,7 @@ class BLEMonitorBinarySensorEntityDescription(
'H5052' : 'Govee',
'H5071' : 'Govee',
'H5074' : 'Govee',
'H5106' : 'Govee',
'H5178' : 'Govee',
'H5178-outdoor' : 'Govee',
'H5179' : 'Govee',
Expand Down
2 changes: 1 addition & 1 deletion custom_components/ble_monitor/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
"btsocket>=0.2.0",
"pyric>=0.1.6.3"
],
"version": "12.1.1"
"version": "12.2.0"
}
18 changes: 18 additions & 0 deletions custom_components/ble_monitor/test/test_govee_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,24 @@ def test_Govee_H5075_double(self):
assert sensor_msg["battery"] == 100
assert sensor_msg["rssi"] == -77

def test_Govee_H5106(self):
"""Test Govee H5106 parser."""
data_string = "043e46020102018ef1f645b5c63a0201060D09475648353130365f34423939030388EC09ff010001010deeaa6f1aff4c000215494e54454c4c495f524f434b535f48575075f2ff0ccc"
data = bytes(bytearray.fromhex(data_string))
# pylint: disable=unused-variable
ble_parser = BleParser()
sensor_msg, tracker_msg = ble_parser.parse_raw_data(data)

assert sensor_msg["firmware"] == "Govee"
assert sensor_msg["type"] == "H5106"
assert sensor_msg["mac"] == "C6B545F6F18E"
assert sensor_msg["packet"] == "no packet id"
assert sensor_msg["data"]
assert sensor_msg["temperature"] == 23.3
assert sensor_msg["humidity"] == 74.5
assert sensor_msg["pm2.5"] == 7
assert sensor_msg["rssi"] == -52

def test_Govee_H5178_sensor_0(self):
"""Test Govee H5178 parser."""
data_string = "043E2B0201000045C5DF38C1A41F0A09423531373843353435030388EC0201050CFF010001010003A00F640000BF"
Expand Down
19 changes: 19 additions & 0 deletions docs/_devices/Govee_H5106.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
manufacturer: Govee
name: Thermometer Smart Air Quality Monitor
model: H5106
image: Govee_H5106.png
physical_description: Rounded rectangular body with screen
broadcasted_properties:
- temperature
- humidity
- pm2.5
- rssi
broadcasted_property_notes:
broadcast_rate:
active_scan: true
encryption_key:
custom_firmware:
notes:
- actve scan needs to be enabled in the BLE Monitor settings for this sensor to work.
---
Binary file added docs/assets/images/Govee_H5106.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.