diff --git a/preload/apps/ptt/__init__.py b/preload/apps/ptt/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f8f305ec385d0d53413be74f87ba28d2807e06f3 --- /dev/null +++ b/preload/apps/ptt/__init__.py @@ -0,0 +1,398 @@ +import ble_hid +import buttons +import max86150 +import leds +import vibra +import display +import simple_menu +import color +import config + +from adafruit_hid.keyboard import Keyboard +from adafruit_hid.keycode import Keycode + +from adafruit_hid.consumer_control_code import ConsumerControlCode +from adafruit_hid.consumer_control import ConsumerControl + +import json +import os +import gc +import time + + +keys = [ + "ESCAPE", + "SPACEBAR", + "CAPS_LOCK", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "PRINT_SCREEN", + "SCROLL_LOCK", + "PAUSE", + "APPLICATION", + "POWER", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "LEFT_CONTROL", + "LEFT_SHIFT", + "LEFT_ALT", + "LEFT_GUI", + "RIGHT_CONTROL", + "RIGHT_SHIFT", + "RIGHT_ALT", + "RIGHT_GUI", +] + +codes = [ + "RECORD", + "FAST_FORWARD", + "REWIND", + "SCAN_NEXT_TRACK", + "SCAN_PREVIOUS_TRACK", + "STOP", + "EJECT", + "PLAY_PAUSE", + "MUTE", + "VOLUME_DECREMENT", + "VOLUME_INCREMENT", +] + +KEYBOARD = "Keyboard" +CONSUMER = "Consumer Control" +classes = [KEYBOARD, CONSUMER] + +CONFIG_NAME = "ptt.json" + + +def ble_check(): + ble_enabled = False + try: + active = config.get_string("ble_enable") + if active.lower() == "true" or active == "1": + ble_enabled = True + except OSError: + pass + + if not ble_enabled: + disp.clear() + disp.print("BLE", posy=0, fg=color.BLUE) + disp.print("disabled", posy=0, posx=45, fg=color.RED) + + disp.print("Enable BLE", posy=40) + disp.print("in BLE Aapp", posy=60) + disp.update() + while buttons.read() == 0: + time.sleep(0.1) + os.exit() + + +def ble_hid_check(): + ble_hid_enabled = False + try: + active = config.get_string("ble_hid_enable") + if active.lower() == "true" or active == "1": + ble_hid_enabled = True + except OSError: + pass + + if not ble_hid_enabled: + disp.clear() + disp.print("HID", posy=0, fg=color.BLUE) + disp.print("disabled", posy=0, posx=45, fg=color.RED) + + disp.print("Enable HID", posy=40) + disp.print("in HID App", posy=60) + disp.update() + while buttons.read() == 0: + time.sleep(0.1) + os.exit() + + +disp = display.open() +ble_check() +ble_hid_check() +connected = False +blocked = False + + +def save_config(): + open(CONFIG_NAME, "w").write(json.dumps(config)) + + +config = { + "key": "LEFT_CONTROL", + "code": "PLAY_PAUSE", + "class": KEYBOARD, + "optical_sensor": True, + "toggle": False, +} + + +try: + config.update(json.loads(open(CONFIG_NAME, "r").read())) +except Exception as e: + print(e) + print("Writing default config") + save_config() + + +print(config) + + +class ProximitySensor: + def __init__(self, callback): + self.callback = callback + self.pressed = False + + def open(self): + def callback(datasets): + if len(datasets) > 0 and self.pressed: + pressed = True + for val in datasets: + # Threshold of proximity interrupt: 32 * (2 ** 10) + if val.infrared < 32 * (2 ** 10) / 2: + pressed = False + if not pressed: + self.pressed = pressed + self.callback(self.pressed) + + def prox(): + self.pressed = True + self.callback(self.pressed) + + self.sensor = max86150.MAX86150( + callback=callback, prox_callback=prox, sample_rate=400 + ) + + def close(self): + if self.self is not None: + self.sensor.close() + self.sensor = None + + +class PTTMenu(simple_menu.Menu): + def on_select(self, name, index): + if config["class"] == KEYBOARD: + config["key"] = name + print(gc.mem_free()) + elif config["class"] == CONSUMER: + config["code"] = name + save_config() + self.exit() + + +class HIDClassMenu(simple_menu.Menu): + def on_select(self, name, index): + config["class"] = name + save_config() + self.exit() + + +class OpticalSensorMenu(simple_menu.Menu): + def on_select(self, name, index): + if name == "Enable": + config["optical_sensor"] = True + elif name == "Disable": + config["optical_sensor"] = False + save_config() + self.exit() + + +class ToggleMenu(simple_menu.Menu): + def on_select(self, name, index): + if name == "Enable": + config["toggle"] = True + elif name == "Disable": + config["toggle"] = False + save_config() + self.exit() + + +class MainMenu(simple_menu.Menu): + def on_select(self, name, index): + print(name) + if name == "PTT Key": + if config["class"] == KEYBOARD: + m = PTTMenu(keys) + try: + m.idx = keys.index(config["key"]) + except Exception as e: + print(e) + elif config["class"] == CONSUMER: + m = PTTMenu(codes) + try: + m.idx = codes.index(config["code"]) + except Exception as e: + print(e) + elif name == "HID Class": + m = HIDClassMenu(classes) + try: + m.idx = classes.index(config["class"]) + except Exception as e: + print(e) + elif name == "Optical Sensor": + if config["optical_sensor"]: + m = OpticalSensorMenu(("Enabled", "Disable", "", "")) + else: + m = OpticalSensorMenu(("Enable", "Disabled", "", "")) + m.idx = 1 + elif name == "Key Toggle": + if config["toggle"]: + m = ToggleMenu(("Enabled", "Disable", "", "")) + else: + m = ToggleMenu(("Enable", "Disabled", "", "")) + m.idx = 1 + + m.run() + self.exit() + + +def push(): + global connected + if config["class"] == KEYBOARD: + key = config["key"] + if hasattr(Keycode, key): + # k.press(192) + try: + if config["toggle"]: + k.send(getattr(Keycode, key)) + else: + k.press(getattr(Keycode, key)) + except OSError as e: + if e.args[0] == 5: + connected = False + return + else: + raise e + elif config["class"] == CONSUMER: + code = config["code"] + if hasattr(ConsumerControlCode, code): + try: + if config["toggle"]: + c.send(getattr(ConsumerControlCode, code)) + c.send(0) + else: + c.send(getattr(ConsumerControlCode, code)) + except OSError as e: + if e.args[0] == 5: + connected = False + return + else: + raise e + + leds.set_rocket(1, 31) + vibra.vibrate(20) + disp.print("Talk now", posy=30, font=4, fg=color.RED) + disp.update() + disp.backlight(20) + + +def release(): + global connected + key = config["key"] + if config["class"] == KEYBOARD: + if hasattr(Keycode, key): + # k.release(192) + try: + if config["toggle"]: + k.send(getattr(Keycode, key)) + else: + k.release(getattr(Keycode, key)) + except OSError as e: + if e.args[0] == 5: + connected = False + else: + raise e + + elif config["class"] == CONSUMER: + try: + if config["toggle"]: + code = config["code"] + c.send(getattr(ConsumerControlCode, code)) + c.send(0) + else: + c.send(0) + except OSError as e: + if e.args[0] == 5: + connected = False + else: + raise e + + leds.set_rocket(1, 0) + disp.backlight(0) + + +def prox_callback(state): + if blocked: + return + if not connected: + return + + if "optical_sensor" in config and config["optical_sensor"]: + if state: + push() + else: + release() + + +sensor = ProximitySensor(prox_callback).open() + +disp.clear() +disp.print("PushToTalk", posy=0) +disp.print("via BLE", posy=20, fg=color.BLUE) +disp.print(" Menu ->", posy=40) +disp.print("<- PTT", posy=60, fg=color.RED) +disp.update() + +time.sleep(3) + +b_old = buttons.read() +while True: + if not connected: + try: + k = Keyboard(ble_hid.devices) + c = ConsumerControl(ble_hid.devices) + connected = True + disp.clear().update() + disp.backlight(0) + except OSError as e: + if e.args[0] == 5: + disp.backlight(20) + disp.clear() + disp.print("Waiting for") + disp.print("BLE Connection", posy=20, fg=color.BLUE) + disp.update() + else: + raise e + else: + b_new = buttons.read() + if not b_old == b_new: + b_old = b_new + if b_new == buttons.BOTTOM_LEFT: + push() + elif b_new == buttons.TOP_RIGHT: + disp.backlight(20) + blocked = True + MainMenu(("PTT Key", "HID Class", "Optical Sensor", "Key Toggle")).run() + blocked = False + disp.clear().update() + disp.backlight(0) + else: + release() + + time.sleep(0.05) diff --git a/preload/apps/ptt/metadata.json b/preload/apps/ptt/metadata.json new file mode 100644 index 0000000000000000000000000000000000000000..1eca3130701b85f0f345fdcc5f167dcf1fb11b67 --- /dev/null +++ b/preload/apps/ptt/metadata.json @@ -0,0 +1 @@ +{"author": "card10 contributors", "name": "PTT", "description": "Push-To-Talk via BLE", "category": "Hardware", "revision": -1, "source":"preload"}