/ MagTag_Tides / code.py
code.py
1 # SPDX-FileCopyrightText: 2020 Carter Nelson for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 import time 6 import terminalio 7 import displayio 8 import adafruit_imageload 9 from adafruit_display_text import label 10 from adafruit_bitmap_font import bitmap_font 11 from adafruit_magtag.magtag import MagTag 12 13 # --| USER CONFIG |-------------------------- 14 STATION_ID = ( 15 "9447130" # tide location, find yours here: https://tidesandcurrents.noaa.gov/ 16 ) 17 METRIC = False # set to True for metric units 18 VSCALE = 2 # pixels per ft or m 19 DAILY_UPDATE_HOUR = 3 # 24 hour format 20 DST_ON = True # Day Light Saving currently active? 21 # ------------------------------------------- 22 23 # don't change these 24 PLOT_WIDTH = 116 25 PLOT_HEIGHT = 116 26 PLOT_X = 174 27 PLOT_Y = 6 28 PLOT_Y_SCALE = round(PLOT_HEIGHT / (4 * VSCALE)) 29 DATE_FONT = bitmap_font.load_font("/fonts/Kanit-Black-24.bdf") 30 TIME_FONT = bitmap_font.load_font("/fonts/Kanit-Medium-20.bdf") 31 32 # our MagTag 33 magtag = MagTag() 34 magtag.json_path = ["predictions"] 35 36 # ---------------------------- 37 # Grid overlay for plot 38 # ---------------------------- 39 grid_bmp, grid_pal = adafruit_imageload.load("/bmps/tides_bg_land.bmp") 40 grid_pal.make_transparent(1) 41 grid_overlay = displayio.TileGrid(grid_bmp, pixel_shader=grid_pal) 42 43 # ---------------------------- 44 # Tide plot (bitmap, palette, tilegrid) 45 # ---------------------------- 46 tide_plot = displayio.Bitmap(PLOT_WIDTH, PLOT_HEIGHT, 4) 47 48 tide_pal = displayio.Palette(4) 49 tide_pal[0] = 0x000000 # black 50 tide_pal[1] = 0x555555 # dark gray 51 tide_pal[2] = 0xAAAAAA # light gray 52 tide_pal[3] = 0xFFFFFF # white 53 tide_pal.make_transparent(3) 54 55 tide_tg = displayio.TileGrid(tide_plot, pixel_shader=tide_pal, x=PLOT_X, y=PLOT_Y) 56 57 # ---------------------------- 58 # Plot scale labels 59 # ---------------------------- 60 plot_y_pos = label.Label(terminalio.FONT, text="+99", color=0x000000) 61 plot_y_pos.text = "{:>3}".format(PLOT_Y_SCALE) 62 plot_y_pos.anchor_point = (1.0, 0.5) 63 plot_y_pos.anchored_position = (178, 34) 64 65 plot_y_neg = label.Label(terminalio.FONT, text="-99", color=0x000000) 66 plot_y_neg.text = "{:>3}".format(-1 * PLOT_Y_SCALE) 67 plot_y_neg.anchor_point = (1.0, 0.5) 68 plot_y_neg.anchored_position = (178, 92) 69 70 plot_y_labels = displayio.Group() 71 plot_y_labels.append(plot_y_pos) 72 plot_y_labels.append(plot_y_neg) 73 74 # ---------------------------- 75 # Date label 76 # ---------------------------- 77 date_label = displayio.Group() 78 date_text = [label.Label(DATE_FONT, text="A", color=0xFFFFFF) for _ in range(5)] 79 y_offset = 8 80 for text in date_text: 81 date_label.append(text) 82 text.anchor_point = (0.5, 0) 83 text.anchored_position = (20, y_offset) 84 y_offset += 23 85 86 # ---------------------------- 87 # HiLo Times and Icons 88 # ---------------------------- 89 tide_info = displayio.Group() 90 91 hilo_times = [label.Label(TIME_FONT, text="12:34 P", color=0x000000) for _ in range(4)] 92 y_offset = 18 93 for hilo in hilo_times: 94 tide_info.append(hilo) 95 hilo.hidden = True 96 hilo.anchor_point = (1, 0.5) 97 hilo.anchored_position = (158, y_offset) 98 y_offset += 28 99 100 icon_bmp, icon_pal = adafruit_imageload.load("/bmps/tides_icons.bmp") 101 icon_pal.make_transparent(1) 102 hilo_icons = [ 103 displayio.TileGrid( 104 icon_bmp, 105 pixel_shader=icon_pal, 106 width=1, 107 height=1, 108 tile_width=24, 109 tile_height=24, 110 ) 111 for _ in range(4) 112 ] 113 y_offset = 6 114 for icon in hilo_icons: 115 tide_info.append(icon) 116 icon.hidden = True 117 icon.x = 46 118 icon.y = y_offset 119 y_offset += 28 120 121 # ---------------------------- 122 # Station ID 123 # ---------------------------- 124 station_info = label.Label( 125 terminalio.FONT, text="STATION ID: " + STATION_ID, color=0x000000 126 ) 127 station_info.anchor_point = (1, 1) 128 station_info.anchored_position = (158, 126) 129 130 # ---------------------------- 131 # Add all the graphic layers 132 # ---------------------------- 133 magtag.splash.append(tide_tg) 134 magtag.splash.append(grid_overlay) 135 magtag.splash.append(plot_y_labels) 136 magtag.splash.append(tide_info) 137 magtag.splash.append(date_label) 138 magtag.splash.append(station_info) 139 140 # ///////////////////////////////////////////////////////////////////////// 141 142 143 def get_data_source_url(station=STATION_ID, metric=METRIC, hilo_only=True): 144 """Build and return the URL for the tides API.""" 145 date = "{}{:02}{:02}".format(now.tm_year, now.tm_mon, now.tm_mday) 146 147 URL = "https://api.tidesandcurrents.noaa.gov/api/prod/datagetter?format=json" 148 URL += "&product=predictions" 149 URL += "&interval=hilo" if hilo_only else "" 150 URL += "&datum=mllw" # MLLW = "tides" 151 URL += "&units=metric" if metric else "&units=english" 152 URL += "&time_zone=lst_ldt" if DST_ON else "&time_zone=lst" 153 URL += "&begin_date=" + date 154 URL += "&end_date=" + date 155 URL += "&station=" + station 156 157 return URL 158 159 160 def get_tide_data(): 161 """Fetch JSON tide data and return parsed results in a list.""" 162 # Get raw JSON data 163 magtag.url = get_data_source_url(hilo_only=False) 164 raw_data = magtag.fetch() 165 166 # Results will be stored in a list that is PLOT_WIDTH long 167 new_tide_data = [PLOT_HEIGHT - 1] * PLOT_WIDTH 168 169 # Convert raw data to display coordinates 170 for data in raw_data: 171 _, t = data["t"].split(" ") # date and time 172 h, m = t.split(":") # hours and minutes 173 v = data["v"] # water level 174 x = round((PLOT_WIDTH - 1) * (60 * float(h) + float(m)) / 1434) 175 y = (PLOT_HEIGHT // 2) - round(VSCALE * float(v)) 176 y = 0 if y < 0 else y 177 y = PLOT_HEIGHT - 1 if y >= PLOT_HEIGHT else y 178 new_tide_data[x] = y 179 180 return new_tide_data 181 182 183 def get_hilo_data(): 184 """Get high / low times.""" 185 # Get raw JSON data 186 magtag.url = get_data_source_url(hilo_only=True) 187 188 return magtag.fetch() 189 190 191 def show_today(): 192 """Display month and day.""" 193 month_text = ( 194 "JAN", 195 "FEB", 196 "MAR", 197 "APR", 198 "MAY", 199 "JUN", 200 "JUL", 201 "AUG", 202 "SEP", 203 "OCT", 204 "NOV", 205 "DEC", 206 )[now.tm_mon - 1] 207 day_text = "{:2}".format(now.tm_mday) 208 209 date_label[0].text = month_text[0] 210 date_label[1].text = month_text[1] 211 date_label[2].text = month_text[2] 212 date_label[3].text = day_text[0] 213 date_label[4].text = day_text[1] 214 215 216 def plot_tides(): 217 """Graphical plot of water level.""" 218 tide_plot.fill(3) 219 for x in range(PLOT_WIDTH): 220 y = tide_data[x] 221 for yfill in range(y, PLOT_HEIGHT): 222 try: 223 tide_plot[x, yfill] = 2 224 except IndexError: 225 pass 226 tide_plot[x, y] = 0 227 228 229 def show_hilo(): 230 """Show high / low times.""" 231 for i in hilo_icons: 232 i.hidden = True 233 for t in hilo_times: 234 t.hidden = True 235 for i, data in enumerate(hilo_data): 236 # make it visible 237 hilo_icons[i].hidden = False 238 hilo_times[i].hidden = False 239 # icon 240 hilo_icons[i][0] = 0 if data["type"] == "H" else 1 241 # time 242 h, m = data["t"].split(" ")[1].split(":") 243 m = int(m) 244 h = int(h) 245 ampm = "A" if h < 12 else "P" 246 h = h if h < 13 else h - 12 247 hilo_times[i].text = "{:>2}:{:02} {}".format(h, m, ampm) 248 249 250 def time_to_sleep(): 251 """Compute amount of time to sleep.""" 252 # daily event time 253 event_time = time.struct_time( 254 (now[0], now[1], now[2], DAILY_UPDATE_HOUR, 0, 0, -1, -1, now[8]) 255 ) 256 # how long is that from now? 257 remaining = time.mktime(event_time) - time.mktime(now) 258 # is that today or tomorrow? 259 if remaining < 0: # ah its aready happened today... 260 remaining += 24 * 60 * 60 # wrap around to the next day 261 # return it 262 return remaining 263 264 265 # =========== 266 # M A I N 267 # =========== 268 # get current time 269 magtag.get_local_time() 270 now = time.localtime() 271 272 # show today's date 273 show_today() 274 275 # get and plot tide levels 276 tide_data = get_tide_data() 277 plot_tides() 278 279 # get and show hilo tide times 280 hilo_data = get_hilo_data() 281 show_hilo() 282 283 # refresh display 284 time.sleep(magtag.display.time_to_refresh + 1) 285 magtag.display.refresh() 286 time.sleep(magtag.display.time_to_refresh + 1) 287 288 # ZZZZZZzzzzzzzzz 289 now = time.localtime() 290 magtag.exit_and_deep_sleep(time_to_sleep()) 291 # 292 # code.py runs again when board wakes up 293 #