S&B Volcano vaporizer remote control with Pi Pico W
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #!/usr/bin/env python3
  2. # ----------------------------------------------------------------------------
  3. # Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # See <http://www.gnu.org/licenses/>.
  16. # ----------------------------------------------------------------------------
  17. import uasyncio as asyncio
  18. from scan import ble_scan
  19. import time
  20. class StateScan:
  21. def __init__(self, lcd):
  22. self.lcd = lcd
  23. self.lock = asyncio.Lock()
  24. def enter(self, val = None):
  25. n = None
  26. self.results = []
  27. self.current = None
  28. self.menuOff = 0
  29. self.scanner = asyncio.create_task(self.scan())
  30. def exit(self):
  31. self.scanner.cancel()
  32. if self.lock.locked():
  33. self.lock.release()
  34. self.lcd.store_brightness()
  35. return self.results[self.current][4]
  36. async def scan(self):
  37. while True:
  38. new = await ble_scan(None, None, 0.25)
  39. for n in new:
  40. name = n.name()
  41. mac = n.device.addr_hex()
  42. rssi = n.rssi
  43. value = [name, mac, rssi, time.time(), n]
  44. async with self.lock:
  45. found = False
  46. for i in range(0, len(self.results)):
  47. if self.results[i][1] == mac:
  48. found = True
  49. self.results[i][0] = name
  50. self.results[i][2] = rssi
  51. self.results[i][3] = time.time()
  52. self.results[i][4] = n
  53. break
  54. if found == False:
  55. self.results.append(value)
  56. def draw_list(self):
  57. if len(self.results) <= 0:
  58. self.lcd.textC("No devices found yet", int(self.lcd.width / 2), int(self.lcd.height / 2), self.lcd.white)
  59. return
  60. for i, d in enumerate(self.results):
  61. if i < self.menuOff:
  62. continue
  63. off = (i - self.menuOff) * 25 + 30
  64. if off >= (self.lcd.height - 10):
  65. break
  66. selection = " "
  67. if self.current == i:
  68. selection = "->"
  69. name, mac, rssi, timeout, device = self.results[i]
  70. age = int(time.time() - timeout)
  71. s1 = "{}: {}".format(i + 1, name)
  72. s2 = "{} [{}] {} {}".format(selection, mac, rssi, age)
  73. c1 = self.lcd.white
  74. if name == "S&B VOLCANO H":
  75. c1 = self.lcd.green
  76. if self.current == None:
  77. self.current = i
  78. elif name == "STORZ&BICKEL":
  79. c1 = self.lcd.yellow
  80. elif self.current == i:
  81. c1 = self.lcd.red
  82. c2 = self.lcd.white
  83. if self.current == i:
  84. c2 = self.lcd.red
  85. self.lcd.hline(0, off - 3, self.lcd.width, self.lcd.blue)
  86. self.lcd.text(s1, 0, off + 2, c1)
  87. self.lcd.text(s2, 0, off + 12, c2)
  88. async def draw(self):
  89. self.lcd.text("Scanning for Bluetooth devices", 0, 10, self.lcd.red)
  90. keys = self.lcd.buttons()
  91. async with self.lock:
  92. if keys.once("enter") or keys.once("a"):
  93. if self.current < len(self.results):
  94. return 2
  95. elif keys.once("up"):
  96. if self.current == None:
  97. self.current = len(self.results) - 1
  98. else:
  99. self.current -= 1
  100. elif keys.once("down"):
  101. if self.current == None:
  102. self.current = 0
  103. else:
  104. self.current += 1
  105. elif keys.once("x"):
  106. return 10
  107. elif keys.held("left"):
  108. v = self.lcd.curr_brightness - 0.05
  109. if v < 0.05:
  110. v = 0.05
  111. self.lcd.brightness(v)
  112. elif keys.held("right"):
  113. self.lcd.brightness(self.lcd.curr_brightness + 0.05)
  114. elif keys.once("y"):
  115. return 0
  116. if self.current != None:
  117. while self.current < 0:
  118. self.current += len(self.results)
  119. while self.current >= len(self.results):
  120. self.current -= len(self.results)
  121. while self.current < self.menuOff:
  122. self.menuOff -= 1
  123. while self.current >= (self.menuOff + int((self.lcd.height - 30 - 10) / 25)):
  124. self.menuOff += 1
  125. # remove entries after timeout
  126. self.results = [x for x in self.results if (time.time() - x[3]) < 10.0]
  127. # filter out incompatible devices
  128. self.results = [x for x in self.results if (x[0] != None) and (("S&B" in x[0]) or ("STORZ&BICKEL" == x[0]))]
  129. if self.current != None:
  130. if self.current >= len(self.results):
  131. self.current = len(self.results) - 1
  132. self.draw_list()
  133. w = int(self.lcd.width * self.lcd.curr_brightness)
  134. self.lcd.hline(0, 24, w, self.lcd.white)
  135. return -1