Skip to content
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

Initial support for GVH5074 #21

Merged
merged 6 commits into from
Jan 18, 2022
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
3 changes: 2 additions & 1 deletion blerry/blerry_main.be
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ for mac:user_config.keys()
end

# Load model handle functions only if used
var model_drivers = {'GVH5075' : 'blerry_model_GVH5075.be',
var model_drivers = {'GVH5074' : 'blerry_model_GVH5074.be',
'GVH5075' : 'blerry_model_GVH5075.be',
'GVH5072' : 'blerry_model_GVH5075.be',
'GVH5101' : 'blerry_model_GVH5075.be',
'GVH5102' : 'blerry_model_GVH5075.be',
Expand Down
67 changes: 67 additions & 0 deletions blerry/blerry_model_GVH5074.be
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
def handle_GVH5074(value, trigger, msg)
if trigger == details_trigger
var this_device = device_config[value['mac']]
var p = bytes(value['p'])
var i = 0
var adv_len = 0
var adv_data = bytes('')
var adv_type = 0

while i < size(p)
adv_len = p.get(i,1)
adv_type = p.get(i+1,1)
adv_data = p[i+2..i+adv_len]
if (adv_type == 0xFF) && (adv_len == 0x0A)
var last_data = this_device['last_p']
if adv_data == last_data
return 0
else
device_config[value['mac']]['last_p'] = adv_data
end

if this_device['discovery'] && !this_device['done_disc']
publish_sensor_discovery(value['mac'], 'Temperature', 'temperature', '°C')
publish_sensor_discovery(value['mac'], 'Humidity', 'humidity', '%')
publish_sensor_discovery(value['mac'], 'DewPoint', 'temperature', '°C')
publish_sensor_discovery(value['mac'], 'Battery', 'battery', '%')
publish_sensor_discovery(value['mac'], 'RSSI', 'signal_strength', 'dB')
device_config[value['mac']]['done_disc'] = true
end

var output_map = {}
output_map['Time'] = tasmota.time_str(tasmota.rtc()['local'])
output_map['alias'] = this_device['alias']
output_map['mac'] = value['mac']
output_map['via_device'] = device_topic
output_map['RSSI'] = value['RSSI']
if this_device['via_pubs']
output_map['Time_via_' + device_topic] = output_map['Time']
output_map['RSSI_via_' + device_topic] = output_map['RSSI']
end

output_map['Battery'] = adv_data.geti(7, 1)
# .geti() will take the two's complement when it extracts data
output_map['Temperature'] = adv_data.geti(3,2) / 100.0
output_map['Humidity'] = adv_data.get(5, 2) / 100.0

output_map['DewPoint'] = round(get_dewpoint(output_map['Temperature'], output_map['Humidity']), this_device['temp_precision'])
output_map['Temperature'] = round(output_map['Temperature'], this_device['temp_precision'])
output_map['Humidity'] = round(output_map['Humidity'], this_device['humi_precision'])
var this_topic = base_topic + '/' + this_device['alias']
tasmota.publish(this_topic, json.dump(output_map), this_device['sensor_retain'])

if this_device['publish_attributes']
for output_key:output_map.keys()
tasmota.publish(this_topic + '/' + output_key, string.format('%s', output_map[output_key]), this_device['sensor_retain'])
end
end

end
i = i + adv_len + 1
end
end
end

# map function into handles array
device_handles['GVH5074'] = handle_GVH5074
require_active['GVH5074'] = true
80 changes: 80 additions & 0 deletions docs/GVH7074.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Notes for GVH7074

https://github.com/w1gx/govee-ble-scanner/wiki/Sniffing-BLE-advertising-packets was helpful in decoding the GVH5074

Full 'p' strings captured from ESP32 serial monitor
```
02010607030A18F5FE88EC1109476F7665655F48353037345F414141410AFF88EC00650775126402
02010607030A18F5FE88EC1109476F7665655F48353037345F414141410AFF88EC00650769126402
02010607030A18F5FE88EC1109476F7665655F48353037345F414141410AFF88EC0069075C126402
```

## Flags
`02 01 06`

`02` - two bytes

`01` - Type = Flags

`06` - Value (bitwise - 00000110) Bit 1 : "LE General Discoverable Mode", Bit 2: "BR/EDR Not Supported."

## Service Class
`07 03 0A 18 F5 FE 88 EC`

`07` - 7 bytes

`03` - Type = Service Class

`0A 18` -> 18 0A (little endian) - Device Information

`F5 FE` -> FE F5 (little endian) - Dialog Semiconductor GmbH

`88 EC` -> EC 88 (little endian) - unknown

## Complete Local Name
`11 09 47 6F 76 65 65 5F 48 35 30 37 34 5F 41 41 41 41`

`11` - 17 bytes

`09` - Type = Complete Local Name

`47 6F 76 65 65 5F 48 35 30 37 34 5F 41 41 41 41` = Govee_H5074_AAAA (AAAA = last 4 of mac)

## Manufacturer Specific Data
`0A FF 88 EC 00 65 07 75 12 64 02`

`0A` - 10 bytes

`FF` - Type = Manufacturer Specific Data

`88 EC 00` - unknown - same across all 'p' strings

`65 07` -> `07 65` (little endian) = See notes on [temperature](#temperature) below

`75 12` -> `12 75` (little endian) = 4725 in decimal. Divide by 100 to get relative humidity

`64` - 100 in decimal. Assuming this is battery percentage, but not sure.

`02` - unknown

## Temperature
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can edit this section now if the .geti is working appropriately.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Two's complement is so close to what you figured out. It's subtracting the temp value from 0x10000 instead of from 0xFFFF!

The GVH5074 uses [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement#Converting_to_two's_complement_representation) for negative numbers.
> In two's complement notation, a non-negative number is represented by its ordinary binary representation; in this case, the most significant bit is 0. Though, the range of numbers represented is not the same as with unsigned binary numbers. For example, an 8-bit unsigned number can represent the values 0 to 255 (11111111). However a two's complement 8-bit number can only represent positive integers from 0 to 127 (01111111), because the rest of the bit combinations with the most significant bit as '1' represent the negative integers −1 to −128.
>
> The two's complement operation is the additive inverse operation, so negative numbers are represented by the two's complement of the absolute value.

The berry language has a `geti` [method](https://github.com/berry-lang/berry/wiki/Chapter-7#get-geti-methods) that will return a signed value.

> Read a 1/2/4 bytes value from any offset in the bytes array. The standard mode is little endian, if you specify a negative size it enables big endian. `get` returns unsigned values, while `geti` returns signed values.
> `b.geti(<offset>, <size>) -> bytes object`

In order to get the temperature in °C, use the `geti` method and then divide by 100.0
```
# Example
adv_data = bytes('88EC0052FA6E166402')
temp = adv_data.geti(3,2) / 100.0
```
Output
```
-14.54
```