prototype.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. ## RaydonEye uses BLE LED Button Service (LBS) UUID 00001523-1212-EFDE-1523-785FEABCD123
  2. ## [!] ble only supports a single connection, if RadonEye App is running this will fail
  3. import BLE_GATT
  4. import time
  5. from gi.repository import GLib
  6. import struct
  7. import subprocess
  8. dev = None
  9. # [!] To get this to work bluetoothctl needs to connect to the device before using BLE_GATT.connect
  10. def ble_ctl(addr:str):
  11. """
  12. use bluetoothctl interactively: scan on, ... wait ..., scan off, connect CF:CD:27:79:55:6B, exit
  13. subprocess has a relevant bug in line-buffering, see issue 21471
  14. """
  15. ctl = subprocess.Popen(['bluetoothctl'], stdin = subprocess.PIPE)
  16. def writeHelper(proc, str, delay = 1):
  17. proc.stdin.write(str.encode('utf-8'))
  18. proc.stdin.flush()
  19. time.sleep(delay)
  20. # send bluetoothctl commands interactively
  21. writeHelper(ctl, "scan on\n", 4)
  22. writeHelper(ctl, "scan off\n")
  23. writeHelper(ctl, "connect " + addr + "\n", 3)
  24. writeHelper(ctl, "exit\n")
  25. # wait for child subprocess to terminate
  26. while True:
  27. if None != ctl.poll():
  28. break
  29. time.sleep(.1)
  30. def radonEye_toFloat(res):
  31. """
  32. pulls a IEEE754 float representing the most recent radon sample in picocuries per
  33. liter from the LBS Read Characteristic, the sample is updated 30 times an hour
  34. RadonEye stores in 4 bytes, big-endian starting at offset 3 of LBS "response"
  35. """
  36. return struct.unpack('<f', bytes(res[2:6]))
  37. # LED Button Service - Write 00001524-1212-EFDE-1523-785FEABCD123
  38. def push_button():
  39. print("timeout, pushing LBS button")
  40. dev.char_write('00001524-1212-EFDE-1523-785FEABCD123', b'\x50')
  41. return True
  42. def notify_handler(value):
  43. print(" ".join(hex(x) for x in value))
  44. print(radonEye_toFloat(value), " picocuries per liter")
  45. while True:
  46. radonEyeMac = 'CF:CD:27:79:55:6B'
  47. try:
  48. # Use Central Role BlueZ D-Bus API (BLE-GATT) to connect to peripheral (RadonEye)
  49. dev = BLE_GATT.Central(radonEyeMac)
  50. break
  51. except:
  52. print("device not found, retrying...\n")
  53. # using OS time take a break for a tenth of a second, then try bluetoothctl commands
  54. time.sleep(.1)
  55. ble_ctl(radonEyeMac)
  56. continue
  57. # Open connection, do not disconnect, BLE only supports a single open connection
  58. dev.connect()
  59. # LED Button Service - Notify/Read 00001525-1212-EFDE-1523-785FEABCD123
  60. dev.on_value_change('00001525-1212-EFDE-1523-785FEABCD123', notify_handler)
  61. GLib.timeout_add_seconds(12, push_button)
  62. dev.wait_for_notifications()