Skip to content

Commit

Permalink
Adds Switchbot Contact and Motion Sensors (WoContact & WoPresence) (#4)
Browse files Browse the repository at this point in the history
* initial switchbot contact sensor support

* initial WoPresence support (switchbot motion)

* add WoPresence to example user_config

* tiny fix
  • Loading branch information
tony-fav committed Dec 19, 2021
1 parent fed945c commit 61680d3
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 61 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*secrets*
*secrets*
blerry_dev.be
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ Rule1 ON System#Boot DO br load('blerry.be') ENDON

| Model String | Mac Example | Description |
| ------------ | ----------- | ----------- |
| `'ATCpvvx'` | `'A4C138CCCCCC'` | Xiaomi sensors on ATC or pvvx firmware with "Custom" advertisement |
| `'GVH5075'` | `'494207AAAAAA'` | Govee H5075. Should work for H5072 as well (untested). |
| `'IBSTH2'` | `'494208DDDDDD'` | Inkbird IBSTH2 with and without humidity. Should work for IBSTH1 as well (untested). |
| `'WoSensorTH'` | `'D4E4A3BBBBBB/1'` | Switchbot temperature and humidity sensor. |
| `'ATCpvvx'` | `'A4C138XXXXXX'` | Xiaomi sensors on ATC or pvvx firmware with "Custom" advertisement |
| `'GVH5075'` | `'494207XXXXXX'` | Govee H5075. Should work for H5072 as well (untested). |
| `'IBSTH2'` | `'494208XXXXXX'` | Inkbird IBSTH2 with and without humidity. Should work for IBSTH1 as well (untested). |
| `'WoSensorTH'` | `'D4E4A3XXXXXX/1'` | Switchbot temperature and humidity sensor. |
| `'WoContact'` | `'D4BD28XXXXXX/1'` | Switchbot contact sensor (also has motion, binary lux, and a button). |
| `'WoPresence'` | `'FC7CADXXXXXX/1'` | Switchbot motion sensor (also has binary lux). |


## Intro Video by @digiblur (Thanks Travis!)
Expand Down
4 changes: 3 additions & 1 deletion blerry/blerry.be
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ var user_config = {'A4C138AAAAAA': {'alias': 'trial_govee5075', 'model': 'GVH507
'A4C138BBBBBB': {'alias': 'other_govee5075', 'model': 'GVH5075', 'via_pubs': false},
'A4C138CCCCCC': {'alias': 'trial_ATCpvvx', 'model': 'ATCpvvx', 'discovery': true, 'use_lwt': true},
'494208DDDDDD': {'alias': 'trial_inkbird', 'model': 'IBSTH2', 'discovery': true},
'D4E4A3BBBBBB/1': {'alias': 'dev_switchbot', 'model': 'WoSensorTH'}}
'D4E4A3BBBBBB/1': {'alias': 'sbot_TH', 'model': 'WoSensorTH'},
'D4BD28AAAAAA/1': {'alias': 'sbot_contact', 'model': 'WoContact'},
'FC7CADCCCCCC/1': {'alias': 'sbot_motion', 'model': 'WoPresence'}}
var base_topic = 'tele/tasmota_blerry' # where to publish the sensor data


Expand Down
4 changes: 3 additions & 1 deletion blerry/blerry_main.be
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ var model_drivers = {'GVH5075' : 'blerry_model_GVH5075.be',
'pvvx' : 'blerry_model_ATCpvvx.be',
'IBSTH1' : 'blerry_model_IBSTH2.be',
'IBSTH2' : 'blerry_model_IBSTH2.be',
'WoSensorTH': 'blerry_model_WoSensorTH.be'}
'WoSensorTH': 'blerry_model_WoSensorTH.be',
'WoContact' : 'blerry_model_WoContact.be',
'WoPresence': 'blerry_model_WoPresence.be'}
var models = {}
for mac:user_config.keys()
models[model_drivers[device_config[mac]['model']]] = true
Expand Down
101 changes: 101 additions & 0 deletions blerry/blerry_model_WoContact.be
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Switchbot Contact Sensor
# https://github.com/OpenWonderLabs/python-host/wiki/Contact-Sensor-BLE-open-API
# Tested on Contact Sensor firmware v1.1
def handle_WoContact(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 == 0x16) && (adv_len == 0x0C) && (adv_data[0..1] == bytes('3DFD'))
# print('----------------------------------------')
# print('PIR :', (adv_data[ 3] & 0x40) >> 6) # 0 = no motion, 1 = motion
# print('Battery:', (adv_data[ 4] & 0x7F) >> 0) # percentage
# print('HAL :', (adv_data[ 5] & 0x06) >> 1) # 0 = closed, 1 = open, 2 = open longer than timout (in app choosable)
# print('LUX :', (adv_data[ 5] & 0x01) >> 0) # 0 = dark, 1 = bright (in app calibratable)
# print('Button :', (adv_data[10] & 0x0F) >> 0) # 1 through 15 with rollover.
# print('----------------------------------------')
# Because there is so much extra info in the data that does change, we do last_data differently than normal.
var this_data = [(adv_data[ 3] & 0x40) >> 6, adv_data[ 4] & 0x7F, (adv_data[ 5] & 0x06) >> 1, adv_data[ 5] & 0x01, adv_data[10] & 0x0F] # PIR, Bat, HAL, Lux, Btn
var last_data = this_device['last_p']
if last_data == bytes('')
device_config[value['mac']]['last_p'] = this_data
return 0
end
if this_data == last_data
return 0
else
device_config[value['mac']]['last_p'] = this_data
end
if this_device['discovery'] && !this_device['done_disc']
publish_binary_sensor_discovery(value['mac'], 'Motion', 'motion')
publish_sensor_discovery(value['mac'], 'Battery', 'battery', '%')
publish_binary_sensor_discovery(value['mac'], 'Contact', 'opening')
publish_binary_sensor_discovery(value['mac'], 'Lux', 'light')
publish_binary_sensor_discovery(value['mac'], 'Button', 'none')
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
if this_data[0] > 0
output_map['Motion'] = 'ON'
else
output_map['Motion'] = 'OFF'
end
output_map['Battery'] = this_data[1]
if this_data[2] > 0
output_map['Contact'] = 'ON'
else
output_map['Contact'] = 'OFF'
end
if this_data[3] > 0
output_map['Lux'] = 'ON'
else
output_map['Lux'] = 'OFF'
end
if this_data[4] != last_data[4]
output_map['Button'] = 'ON'
else
output_map['Button'] = 'OFF'
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
# clear button
if output_map['Button'] == 'ON'
output_map['Button'] = 'OFF'
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
end
i = i + adv_len + 1
end
end
end

# map function into handles array
device_handles['WoContact'] = handle_WoContact
require_active['WoContact'] = true
71 changes: 71 additions & 0 deletions blerry/blerry_model_WoPresence.be
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Switchbot Motion Sensor
# https://github.com/OpenWonderLabs/python-host/wiki/Motion-Sensor-BLE-open-API
# Tested on Motion Sensor firmware v1.3
def handle_WoPresence(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 == 0x16) && (adv_len == 0x09) && (adv_data[0..1] == bytes('3DFD'))
var this_data = [(adv_data[3] & 0x40) >> 6, adv_data[4] & 0x7F, adv_data[7] & 0x03]
var last_data = this_device['last_p']
if last_data == bytes('')
device_config[value['mac']]['last_p'] = this_data
return 0
end
if this_data == last_data
return 0
else
device_config[value['mac']]['last_p'] = this_data
end
if this_device['discovery'] && !this_device['done_disc']
publish_binary_sensor_discovery(value['mac'], 'Motion', 'motion')
publish_sensor_discovery(value['mac'], 'Battery', 'battery', '%')
publish_binary_sensor_discovery(value['mac'], 'Lux', 'light')
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
if this_data[0] > 0
output_map['Motion'] = 'ON'
else
output_map['Motion'] = 'OFF'
end
output_map['Battery'] = this_data[1]
if this_data[2] == 2
output_map['Lux'] = 'ON'
elif this_data[2] == 1
output_map['Lux'] = 'OFF'
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['WoPresence'] = handle_WoPresence
require_active['WoPresence'] = true
54 changes: 0 additions & 54 deletions dev_helpers/blerry_dev_WoSensorTH.be

This file was deleted.

0 comments on commit 61680d3

Please sign in to comment.