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