/ BLE_Buzzy_Box / code.py
code.py
1 # SPDX-FileCopyrightText: 2020 Liz Clark for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 import time 6 import board 7 import busio 8 import neopixel 9 import adafruit_drv2605 10 import adafruit_ble 11 from adafruit_ble.advertising.standard import SolicitServicesAdvertisement 12 from adafruit_ble.services.standard import CurrentTimeService 13 from adafruit_ble_apple_notification_center import AppleNotificationCenterService 14 from digitalio import DigitalInOut, Direction 15 16 # setup for onboard NeoPixel 17 pixel_pin = board.NEOPIXEL 18 num_pixels = 1 19 20 pixel = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.3, auto_write=False) 21 22 # setup for haptic motor driver 23 i2c = busio.I2C(board.SCL, board.SDA) 24 drv = adafruit_drv2605.DRV2605(i2c) 25 26 # onboard blue LED 27 blue_led = DigitalInOut(board.BLUE_LED) 28 blue_led.direction = Direction.OUTPUT 29 30 # setup for BLE 31 ble = adafruit_ble.BLERadio() 32 if ble.connected: 33 for c in ble.connections: 34 c.disconnect() 35 36 advertisement = SolicitServicesAdvertisement() 37 38 # adds ANCS and current time services for BLE to advertise 39 advertisement.solicited_services.append(AppleNotificationCenterService) 40 advertisement.solicited_services.append(CurrentTimeService) 41 42 # state machines 43 current_notification = None # tracks the current notification from ANCS 44 current_notifications = {} # array to hold all current notifications from ANCS 45 cleared = False # state to track if notifications have been cleared from ANCS 46 notification_service = None # holds the array of active notifications from ANCS 47 all_ids = [] # array to hold all of the ids from ANCS 48 hour = 0 # used to track when it is on the hour for the mindfulness reminder 49 mindful = False # state used to track if it is time for mindfulness 50 vibration = 16 # vibration effect being used for the haptic motor 51 blue = (0, 0, 255) # color blue for the NeoPixel 52 purple = (255, 0, 255) # color purple for the NeoPixel 53 red = (255, 0, 0) # color red for the NeoPixel 54 clear = (0, 0, 0) # allows for NeoPixel to be turned 'off' 55 56 # function for blinking NeoPixel 57 # blinks: # of blinks 58 # speed: how fast/slow blinks 59 # color1: first color 60 # color2: second color 61 def blink_pixel(blinks, speed, color1, color2): 62 for _ in range(0, blinks): 63 pixel.fill(color1) 64 pixel.show() 65 time.sleep(speed) 66 pixel.fill(color2) 67 pixel.show() 68 time.sleep(speed) 69 70 # function for haptic motor vibration 71 # num_zzz: # of times vibrates 72 # effect: type of vibration 73 # delay: time between vibrations 74 def vibe(num_zzz, effect, delay): 75 drv.sequence[0] = adafruit_drv2605.Effect(effect) 76 for _ in range(0, num_zzz): 77 drv.play() # play the effect 78 time.sleep(delay) # for 0.5 seconds 79 drv.stop() 80 81 # start BLE 82 ble.start_advertising(advertisement) 83 84 while True: 85 86 blue_led.value = False 87 print("Waiting for connection") 88 89 # NeoPixel is red when not connected to BLE 90 while not ble.connected: 91 blue_led.value = False 92 pixel.fill(red) 93 pixel.show() 94 95 print("Connected") 96 97 while ble.connected: 98 blue_led.value = True # blue LED is on when connected 99 all_ids.clear() 100 for connection in ble.connections: 101 if not connection.paired: 102 # pairs to phone 103 connection.pair() 104 print("paired") 105 # allows connection to CurrentTimeService 106 cts = connection[CurrentTimeService] 107 notification_service = connection[AppleNotificationCenterService] 108 # grabs notifications from ANCS 109 current_notifications = notification_service.active_notifications 110 111 for notif_id in current_notifications: 112 # adds notifications into array 113 notification = current_notifications[notif_id] 114 all_ids.append(notif_id) 115 116 if current_notification and current_notification.removed: 117 # Stop showing the latest and show that there are no new notifications. 118 current_notification = None 119 pixel.fill(clear) 120 pixel.show() 121 122 if not current_notification and not all_ids and not cleared: 123 # updates cleared state for notification 124 cleared = True 125 # turns off NeoPixel when notifications are clear 126 pixel.fill(clear) 127 pixel.show() 128 129 elif all_ids: 130 cleared = False 131 if current_notification and current_notification.id in all_ids: 132 index = all_ids.index(current_notification.id) 133 else: 134 index = len(all_ids) - 1 135 notif_id = all_ids[index] 136 # if there is a notification: 137 if not current_notification or current_notification.id != notif_id: 138 current_notification = current_notifications[notif_id] 139 # parses notification info into a string 140 category = str(notification).split(" ", 1)[0] 141 # haptic motor vibrates 142 vibe(2, vibration, 0.5) 143 # all info for notification is printed to REPL 144 print('-'*36) 145 print("Msg #%d - Category %s" % (notification.id, category)) 146 print("From app:", notification.app_id) 147 if notification.title: 148 print("Title:", notification.title) 149 if notification.subtitle: 150 print("Subtitle:", notification.subtitle) 151 if notification.message: 152 print("Message:", notification.message) 153 # NeoPixel blinks and then stays on until cleared 154 blink_pixel(2, 0.5, purple, clear) 155 pixel.fill(purple) 156 pixel.show() 157 # if it's on the hour: 158 if cts.current_time[4] == hour and not mindful: 159 print(cts.current_time[4]) 160 print("mindful time") 161 # haptic motor vibrates 162 vibe(5, vibration, 1) 163 # NeoPixel blinks and then stays on 164 blink_pixel(5, 1, blue, clear) 165 mindful = True 166 pixel.fill(blue) 167 pixel.show() 168 print("hour = ", hour) 169 # if it's no longer on the hour: 170 if cts.current_time[4] == (hour + 1) and mindful: 171 # NeoPixel turns off 172 mindful = False 173 pixel.fill(clear) 174 pixel.show() 175 print("mindful time over") 176 177 # if BLE becomes disconnected then blue LED turns off 178 # and BLE begins advertising again to reconnect 179 print("Disconnected") 180 blue_led.value = False 181 print() 182 ble.start_advertising(advertisement) 183 notification_service = None