-
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Govee 4 probe meat thermometer
- Loading branch information
Showing
6 changed files
with
256 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# The 5184 uses a two line method. Probe1/2 are published in one packet. Probe3/4 in another. Not always sequential | ||
def handle_GVH5184(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 == 0x14) | ||
var this_part_data = [adv_data.get(9, -1), adv_data.get(10, -2), adv_data.get(12, -2), adv_data.get(14, -1), adv_data.get(15, -2), adv_data.get(17, -2)] | ||
var last_full_data = this_device['last_p'] | ||
var this_full_data = [ 0, [-1, 0, 0, 0, 0, 0], [-1, 0, 0, 0, 0, 0], 0 ] | ||
var seq_num = adv_data.get(8, -1) | ||
if last_full_data == bytes('') | ||
last_full_data = [ -1, [-1, 0, 0, 0, 0, 0], [-1, 0, 0, 0, 0, 0], 0 ] | ||
else | ||
this_full_data = last_full_data | ||
end | ||
if (this_part_data == last_full_data[seq_num]) && (last_full_data[3] < 31) | ||
this_full_data[3]=this_full_data[3]+1 | ||
device_config[value['mac']]['last_p'] = this_full_data.copy() | ||
return 0 | ||
end | ||
this_full_data[0]= adv_data.get(7, -1) | ||
this_full_data[3] = 0 | ||
this_full_data[seq_num]=this_part_data.copy() | ||
device_config[value['mac']]['last_p'] = this_full_data.copy() | ||
if (this_full_data[1][0] < 0) || (this_full_data[2][0]) < 0 | ||
return 0 | ||
end | ||
if this_device['discovery'] && !this_device['done_disc'] | ||
for j: 1 .. 4 | ||
publish_binary_sensor_discovery(value['mac'], ('Temperature_'+str(j)+'_Status'), 'plug') | ||
publish_binary_sensor_discovery(value['mac'], 'Temperature_'+str(j)+'_Alarm', 'heat') | ||
publish_sensor_discovery(value['mac'], 'Temperature_'+str(j), 'temperature', '°C') | ||
publish_sensor_discovery(value['mac'], 'Temperature_'+str(j)+'_Target', 'temperature', '°C') | ||
end | ||
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'] = math.ceil(this_full_data[0]/255.0*100.0) | ||
for j:1 .. 2 | ||
for k:j-1 .. j | ||
var probeset | ||
if (this_full_data[j][3+((k-j)*3)] & 0x80) >> 7 | ||
probeset = ['ON', 'OFF'] | ||
else | ||
probeset = ['OFF', 'OFF'] | ||
end | ||
if (this_full_data[j][3+((k-j)*3)] & 0x40) >> 6 | ||
probeset = ['ON', 'ON'] | ||
end | ||
if this_full_data[j][4+((k-j)*3)]==65535 | ||
probeset = probeset .. 'unavailable' | ||
else | ||
probeset = probeset .. round(this_full_data[j][4+((k-j)*3)]/100.0, this_device['temp_precision']) | ||
end | ||
if this_full_data[j][5+((k-j)*3)]==65535 | ||
probeset = probeset .. 'unavailable' | ||
else | ||
probeset = probeset .. round(this_full_data[j][5+((k-j)*3)]/100.0, this_device['temp_precision']) | ||
end | ||
output_map['Temperature_'+str(j+k)+'_Status'] = probeset[0] | ||
output_map['Temperature_'+str(j+k)+'_Alarm'] = probeset[1] | ||
output_map['Temperature_'+str(j+k)] = probeset[2] | ||
output_map['Temperature_'+str(j+k)+'_Target'] = probeset[3] | ||
end | ||
end | ||
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['GVH5184'] = handle_GVH5184 | ||
require_active['GVH5184'] = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
Working notes - reverse engineering data stream for GVH5184 | ||
|
||
|
||
Working on the code for the 4-probe Govee driver. NOTE - any code is intended as psuedo-code and is not syntax correct. | ||
|
||
The full 'p' string that comes from BLE looks like this: | ||
|
||
0201060303518414FF363E5D01000101E40106FFFFFFFF06FFFFFFFF | ||
|
||
Breaking that down: (ref - https://community.silabs.com/s/article/kba-bt-0201-bluetooth-advertising-data-basics?language=en_US) | ||
|
||
``` | ||
02 01 06 03 03 51 84 14 FF | ||
02 01 06 | ||
-- -- -- <- Two bytes (02), Type = Flags (01), Value (06) (bitwise - 00000110) Bit 1 : “LE General Discoverable Mode” | ||
Bit 2: “BR/EDR Not Supported.” | ||
03 03 51 84 | ||
-- -- -- -- <- Three bytes (03), Type = Service Class (03), Little endian. 84 51 - not in the list of 16Bit UUIDs. Perhaps Manuf cheated and just put model number here instead. It is the 5184 sensor. | ||
14 FF | ||
-- -- <- 20 Bytes (0x14), Type = FF - Manuf. Proprietary data | ||
This is what we were looking for. All data after the 14 FF will be data we can break up for sensor data. | ||
``` | ||
|
||
The remaining string to be manipulated: | ||
|
||
36 3E 5D 01 00 01 01 E4 01 06 FF FF FF FF 06 FF FF FF FF | ||
|
||
``` | ||
1 2 3 (Byte Sequence) | ||
36 3E 5D | ||
-- -- -- <- Last 3 bytes of MAC ADDRESS | ||
4 5 6 7 | ||
01 00 01 01 | ||
-- -- -- -- <- static, unknown, not needed at this time | ||
8 | ||
E4 | ||
-- <- Battery Percentage Value divided by 255 for percentage. | ||
9 | ||
01 | ||
-- <- Sequence number. Can be 01 or 02. 01 is Probe1&2. 02 is Probe3&4. | ||
10 | ||
06 | ||
-- <- Probe1/3 status. 06 is no probe. 86 is probe inserted. C6 when probe has exceeded setpoint | ||
11 12 | ||
FF FF | ||
-- -- <- Probe1/3 temp. FF FF if not inserted. Big-endian 2-byte number. See below. | ||
13 14 | ||
FF FF | ||
-- -- <- Probe1/3 set point for alarm. Same scheme as temp. | ||
15 | ||
06 | ||
-- <- Probe2/4 status. 06 is no probe. 86 is probe inserted. | ||
16 17 | ||
FF FF | ||
-- -- <- Probe2/4 temp. Same scheme as Probe 1/3 | ||
18 19 | ||
FF FF | ||
-- -- <- Probe2/4 set point for alarm. Same scheme as temp. | ||
``` | ||
|
||
We now can build out a sensor data stream to encode and push back through MQTT | ||
|
||
``` | ||
BYTE8 - Battery %. 0x00-0xFF, 0-255 - Battery percentage is (VALUE / 255) | ||
BYTE9 - Sequence. Either 01 or 02. 01 represents Probes 1 & 2. 02 represents probes 3 & 4. | ||
BYTE10 - Probe[A] Status. [A] is 1 when Sequence is 1. [A] is 3 when Sequence is 2. | ||
Possible Values. 06 - no probe inserted. 86 - Probe inserted normal function. C6 - probe temp has exceeded set point. | ||
BYTE11-12 - Probe[A] Temp. Two bytes - big endian - convert to decimal and divide by 100 to obtain temperature in (C). | ||
BYTE13-14 - Probe[A] Setpoint. Two bytes - big endian. Same conversion. Temp to alarm at. Will set status to C6 when temp >= setpoint | ||
BYTE15 - Probe[B] Status. [A] is 1 when Sequence is 1. [A] is 3 when Sequence is 2. | ||
Possible Values. 06 - no probe inserted. 86 - Probe inserted normal function. C6 - probe temp has exceeded set point. | ||
BYTE16-17 - Probe[B] Temp. Two bytes - big endian - convert to decimal and divide by 100 to obtain temperature in (C). | ||
BYTE18-19 - Probe[B] Setpoint. Two bytes - big endian. Same conversion. Temp to alarm at. Will set status to C6 when temp >= setpoint | ||
``` | ||
|
||
|
||
|
||
Building on other driver files, the following approach will be used. | ||
|
||
1) Iterate through the string and find 14 and FF. Assign the remaing 19 bytes to a variable. | ||
|
||
2) We need to check if anything is changed so we don't do needless updates. So we will always need a last_value and current_value. If | ||
they are the same we can end and not update anything. | ||
|
||
3) Declare last_value = stored ['last_value'] | ||
|
||
4) Check to see if last_value is empty - which means we are on our first pass and need to create a placeholder | ||
|
||
5) If last_value does not yet exist - declare "last_value" and assign negative values for later test.) | ||
Last value will be a date structure list with the following values stored in the list. This is a list and not a map | ||
so these names are just for documentation. It will be a list of 12 INT values. | ||
``` | ||
[ Probe1 Status, | ||
Probe1 Temp, | ||
Probe1 Setpoint, | ||
Probe2 Status, | ||
Probe2 Temp, | ||
Probe2 Setpoint | ||
Probe3 Status, | ||
Probe3 Temp, | ||
Probe3 Setpoint, | ||
Probe4 Status, | ||
Probe4 Temp, | ||
Probe4 Setpoint ] | ||
``` | ||
|
||
5) Now we need to see if the current 'p' value is carrying the values for Probe1/2 or Probe3/4. Since we only get half the probes on any pass - we | ||
have to parse out the values and push them into our current_value list based on which sequence we are on. Check for sequence. | ||
|
||
``` | ||
If .get(8,-1) == 1 then #This is BYTE9 which contains the sequence number) | ||
sequence_index=0 | ||
else | ||
sequence_index=6 | ||
``` | ||
|
||
6) Now we can use sequence index as an offset. This will allow us to reuse the same block of code regardless of which sequence we are on. | ||
``` | ||
current_value[sequence_index] = .get((9+sequence_index),-1) | ||
current_value[sequence_index+1] = .get((10+sequence_index),-2) | ||
current_value[sequence_index+2] = .get((12+sequence_index),-2) | ||
current_value[sequence_index+3] = .get((14+sequence_index),-1) | ||
current_value[sequence_index+4] = .get((15+sequence_index),-2) | ||
current_value[sequence_index+5] = .get((17+sequence_index),-2) | ||
``` | ||
|
||
7) Now we can compare current_value to last_value. Both contain a complete set of 12 discrete values. If the same return 0. | ||
|
||
8) If they are not the same we will need to publish the updated data. At this point - it is unclear if there is any advantage or disadvantage of publishing | ||
all 12 pieces of data. If we do discovery on Probe1/2 and Probe3/4 separtely and publish values for each separately - then we would not have entities for | ||
Probe 3/4 if they are not plugged in. TBD. | ||
|