|
4 | 4 |
|
5 | 5 | import os |
6 | 6 | import time |
7 | | -import microcontroller |
8 | 7 | import ssl |
9 | 8 | import wifi |
10 | 9 | import socketpool |
| 10 | +import microcontroller |
11 | 11 | import adafruit_requests |
12 | 12 |
|
13 | 13 | # Initialize WiFi Pool (There can be only 1 pool & top of script) |
@@ -310,284 +310,3 @@ def time_calc(input_time): |
310 | 310 | time.sleep(60) |
311 | 311 | continue |
312 | 312 | time.sleep(sleep_time) |
313 | | - |
314 | | -# Fitbit_ClientID = "YourAppClientID" |
315 | | -# Fitbit_Token = "Long 256 character string (SHA-256)" |
316 | | -# Fitbit_First_Refresh_Token = "64 character string" |
317 | | -# Fitbit_UserID = "UserID authorizing the ClientID" |
318 | | - |
319 | | -Fitbit_ClientID = os.getenv("Fitbit_ClientID") |
320 | | -Fitbit_Token = os.getenv("Fitbit_Token") |
321 | | -# overides nvm first run only |
322 | | -Fitbit_First_Refresh_Token = os.getenv("Fitbit_First_Refresh_Token") |
323 | | -Fitbit_UserID = os.getenv("Fitbit_UserID") |
324 | | - |
325 | | -wifi_ssid = os.getenv("CIRCUITPY_WIFI_SSID") |
326 | | -wifi_pw = os.getenv("CIRCUITPY_WIFI_PASSWORD") |
327 | | - |
328 | | -# Time between API refreshes |
329 | | -# 300 = 5 mins, 900 = 15 mins, 1800 = 30 mins, 3600 = 1 hour |
330 | | -sleep_time = 900 |
331 | | - |
332 | | - |
333 | | -# Converts seconds in minutes/hours/days |
334 | | -def time_calc(input_time): |
335 | | - if input_time < 60: |
336 | | - sleep_int = input_time |
337 | | - time_output = f"{sleep_int:.0f} seconds" |
338 | | - elif 60 <= input_time < 3600: |
339 | | - sleep_int = input_time / 60 |
340 | | - time_output = f"{sleep_int:.0f} minutes" |
341 | | - elif 3600 <= input_time < 86400: |
342 | | - sleep_int = input_time / 60 / 60 |
343 | | - time_output = f"{sleep_int:.1f} hours" |
344 | | - else: |
345 | | - sleep_int = input_time / 60 / 60 / 24 |
346 | | - time_output = f"{sleep_int:.1f} days" |
347 | | - return time_output |
348 | | - |
349 | | - |
350 | | -# Authenticates Client ID & SHA-256 Token to POST |
351 | | -fitbit_oauth_header = {"Content-Type": "application/x-www-form-urlencoded"} |
352 | | -fitbit_oauth_token = "https://api.fitbit.com/oauth2/token" |
353 | | - |
354 | | -# Connect to Wi-Fi |
355 | | -print("\n===============================") |
356 | | -print("Connecting to WiFi...") |
357 | | -requests = adafruit_requests.Session(pool, ssl.create_default_context()) |
358 | | -while not wifi.radio.ipv4_address: |
359 | | - try: |
360 | | - wifi.radio.connect(wifi_ssid, wifi_pw) |
361 | | - except ConnectionError as e: |
362 | | - print("Connection Error:", e) |
363 | | - print("Retrying in 10 seconds") |
364 | | - time.sleep(10) |
365 | | -print("Connected!\n") |
366 | | - |
367 | | -# First run uses settings.toml token |
368 | | -Refresh_Token = Fitbit_First_Refresh_Token |
369 | | - |
370 | | -if debug: |
371 | | - print(f"Top NVM Again (just to make sure): {top_nvm}") |
372 | | - print(f"Settings.toml Initial Refresh Token: {Fitbit_First_Refresh_Token}") |
373 | | - |
374 | | -while True: |
375 | | - # Use Settings.toml refresh token on first run |
376 | | - if top_nvm != Fitbit_First_Refresh_Token: |
377 | | - Refresh_Token = microcontroller.nvm[0:64].decode() |
378 | | - if debug: |
379 | | - # NVM 64 should match Current Refresh Token |
380 | | - print(f"NVM 64: {microcontroller.nvm[0:64].decode()}") |
381 | | - print(f"Current Refresh_Token: {Refresh_Token}") |
382 | | - else: |
383 | | - if debug: |
384 | | - # First run use settings.toml refresh token instead |
385 | | - print(f"Initial_Refresh_Token: {Refresh_Token}") |
386 | | - |
387 | | - try: |
388 | | - if debug: |
389 | | - print("\n-----Token Refresh POST Attempt -------") |
390 | | - fitbit_oauth_refresh_token = ( |
391 | | - "&grant_type=refresh_token" |
392 | | - + "&client_id=" |
393 | | - + str(Fitbit_ClientID) |
394 | | - + "&refresh_token=" |
395 | | - + str(Refresh_Token) |
396 | | - ) |
397 | | - |
398 | | - # ----------------------------- POST FOR REFRESH TOKEN ----------------------- |
399 | | - if debug: |
400 | | - print( |
401 | | - f"FULL REFRESH TOKEN POST:{fitbit_oauth_token}" |
402 | | - + f"{fitbit_oauth_refresh_token}" |
403 | | - ) |
404 | | - print(f"Current Refresh Token: {Refresh_Token}") |
405 | | - # TOKEN REFRESH POST |
406 | | - fitbit_oauth_refresh_POST = requests.post( |
407 | | - url=fitbit_oauth_token, |
408 | | - data=fitbit_oauth_refresh_token, |
409 | | - headers=fitbit_oauth_header, |
410 | | - ) |
411 | | - try: |
412 | | - fitbit_refresh_oauth_json = fitbit_oauth_refresh_POST.json() |
413 | | - |
414 | | - fitbit_new_token = fitbit_refresh_oauth_json["access_token"] |
415 | | - if debug: |
416 | | - print("Your Private SHA-256 Token: ", fitbit_new_token) |
417 | | - fitbit_access_token = fitbit_new_token # NEW FULL TOKEN |
418 | | - |
419 | | - # If current token valid will respond |
420 | | - fitbit_new_refesh_token = fitbit_refresh_oauth_json["refresh_token"] |
421 | | - Refresh_Token = fitbit_new_refesh_token |
422 | | - fitbit_token_expiration = fitbit_refresh_oauth_json["expires_in"] |
423 | | - fitbit_scope = fitbit_refresh_oauth_json["scope"] |
424 | | - fitbit_token_type = fitbit_refresh_oauth_json["token_type"] |
425 | | - fitbit_user_id = fitbit_refresh_oauth_json["user_id"] |
426 | | - if debug: |
427 | | - print("Next Refresh Token: ", Refresh_Token) |
428 | | - |
429 | | - # Store Next Token into NVM |
430 | | - try: |
431 | | - nvmtoken = b"" + fitbit_new_refesh_token |
432 | | - microcontroller.nvm[0:64] = nvmtoken |
433 | | - if debug: |
434 | | - print(f"Next Token for NVM: {nvmtoken.decode()}") |
435 | | - print("Next token written to NVM Successfully!") |
436 | | - except OSError as e: |
437 | | - print("OS Error:", e) |
438 | | - continue |
439 | | - |
440 | | - if debug: |
441 | | - # Extraneous token data for debugging |
442 | | - print("Token Expires in: ", time_calc(fitbit_token_expiration)) |
443 | | - print("Scope: ", fitbit_scope) |
444 | | - print("Token Type: ", fitbit_token_type) |
445 | | - print("UserID: ", fitbit_user_id) |
446 | | - |
447 | | - except KeyError as e: |
448 | | - print("Key Error:", e) |
449 | | - print("Expired token, invalid permission, or (key:value) pair error.") |
450 | | - time.sleep(300) |
451 | | - continue |
452 | | - |
453 | | - # ----------------------------- GET DATA ------------------------------------- |
454 | | - # POST should respond with current & next refresh token we can GET for data |
455 | | - # 64-bit Refresh tokens will "keep alive" SHA-256 token indefinitely |
456 | | - # Fitbit main SHA-256 token expires in 8 hours unless refreshed! |
457 | | - # ---------------------------------------------------------------------------- |
458 | | - detail_level = "1min" # Supported: 1sec | 1min | 5min | 15min |
459 | | - requested_date = "today" # Date format yyyy-MM-dd or today |
460 | | - fitbit_header = { |
461 | | - "Authorization": "Bearer " + fitbit_access_token + "", |
462 | | - "Client-Id": "" + Fitbit_ClientID + "", |
463 | | - } |
464 | | - # Heart Intraday Scope |
465 | | - FITBIT_SOURCE = ( |
466 | | - "https://api.fitbit.com/1/user/" |
467 | | - + Fitbit_UserID |
468 | | - + "/activities/heart/date/today" |
469 | | - + "/1d/" |
470 | | - + detail_level |
471 | | - + ".json" |
472 | | - ) |
473 | | - |
474 | | - print("\nAttempting to GET FITBIT Stats!") |
475 | | - print("===============================") |
476 | | - fitbit_get_response = requests.get(url=FITBIT_SOURCE, headers=fitbit_header) |
477 | | - try: |
478 | | - fitbit_json = fitbit_get_response.json() |
479 | | - intraday_response = fitbit_json["activities-heart-intraday"]["dataset"] |
480 | | - except ConnectionError as e: |
481 | | - print("Connection Error:", e) |
482 | | - print("Retrying in 10 seconds") |
483 | | - |
484 | | - if debug: |
485 | | - print(f"Full API GET URL: {FITBIT_SOURCE}") |
486 | | - print(f"Header: {fitbit_header}") |
487 | | - # print(f"JSON Full Response: {fitbit_json}") |
488 | | - print(f"Intraday Full Response: {intraday_response}") |
489 | | - |
490 | | - try: |
491 | | - # Fitbit's sync to your mobile device & server every 15 minutes in chunks. |
492 | | - # Pointless to poll their API faster than 15 minute intervals. |
493 | | - activities_heart_value = fitbit_json["activities-heart-intraday"]["dataset"] |
494 | | - response_length = len(activities_heart_value) |
495 | | - if response_length >= 15: |
496 | | - activities_timestamp = fitbit_json["activities-heart"][0]["dateTime"] |
497 | | - print(f"Fitbit Date: {activities_timestamp}") |
498 | | - activities_latest_heart_time = fitbit_json["activities-heart-intraday"][ |
499 | | - "dataset" |
500 | | - ][response_length - 1]["time"] |
501 | | - print(f"Fitbit Time: {activities_latest_heart_time[0:-3]}") |
502 | | - print(f"Today's Logged Pulses : {response_length}") |
503 | | - |
504 | | - # Each 1min heart rate is a 60 second average |
505 | | - activities_latest_heart_value0 = fitbit_json[ |
506 | | - "activities-heart-intraday" |
507 | | - ]["dataset"][response_length - 1]["value"] |
508 | | - activities_latest_heart_value1 = fitbit_json[ |
509 | | - "activities-heart-intraday" |
510 | | - ]["dataset"][response_length - 2]["value"] |
511 | | - activities_latest_heart_value2 = fitbit_json[ |
512 | | - "activities-heart-intraday" |
513 | | - ]["dataset"][response_length - 3]["value"] |
514 | | - activities_latest_heart_value3 = fitbit_json[ |
515 | | - "activities-heart-intraday" |
516 | | - ]["dataset"][response_length - 4]["value"] |
517 | | - activities_latest_heart_value4 = fitbit_json[ |
518 | | - "activities-heart-intraday" |
519 | | - ]["dataset"][response_length - 5]["value"] |
520 | | - activities_latest_heart_value5 = fitbit_json[ |
521 | | - "activities-heart-intraday" |
522 | | - ]["dataset"][response_length - 6]["value"] |
523 | | - activities_latest_heart_value6 = fitbit_json[ |
524 | | - "activities-heart-intraday" |
525 | | - ]["dataset"][response_length - 7]["value"] |
526 | | - activities_latest_heart_value7 = fitbit_json[ |
527 | | - "activities-heart-intraday" |
528 | | - ]["dataset"][response_length - 8]["value"] |
529 | | - activities_latest_heart_value8 = fitbit_json[ |
530 | | - "activities-heart-intraday" |
531 | | - ]["dataset"][response_length - 9]["value"] |
532 | | - activities_latest_heart_value9 = fitbit_json[ |
533 | | - "activities-heart-intraday" |
534 | | - ]["dataset"][response_length - 10]["value"] |
535 | | - activities_latest_heart_value10 = fitbit_json[ |
536 | | - "activities-heart-intraday" |
537 | | - ]["dataset"][response_length - 11]["value"] |
538 | | - activities_latest_heart_value11 = fitbit_json[ |
539 | | - "activities-heart-intraday" |
540 | | - ]["dataset"][response_length - 12]["value"] |
541 | | - activities_latest_heart_value12 = fitbit_json[ |
542 | | - "activities-heart-intraday" |
543 | | - ]["dataset"][response_length - 13]["value"] |
544 | | - activities_latest_heart_value13 = fitbit_json[ |
545 | | - "activities-heart-intraday" |
546 | | - ]["dataset"][response_length - 14]["value"] |
547 | | - activities_latest_heart_value14 = fitbit_json[ |
548 | | - "activities-heart-intraday" |
549 | | - ]["dataset"][response_length - 15]["value"] |
550 | | - |
551 | | - print( |
552 | | - f"{latest_15_avg}" |
553 | | - + f"{activities_latest_heart_value14}," |
554 | | - + f"{activities_latest_heart_value13}," |
555 | | - + f"{activities_latest_heart_value12}," |
556 | | - + f"{activities_latest_heart_value11}," |
557 | | - + f"{activities_latest_heart_value10}," |
558 | | - + f"{activities_latest_heart_value9}," |
559 | | - + f"{activities_latest_heart_value8}," |
560 | | - + f"{activities_latest_heart_value7}," |
561 | | - + f"{activities_latest_heart_value6}," |
562 | | - + f"{activities_latest_heart_value5}," |
563 | | - + f"{activities_latest_heart_value4}," |
564 | | - + f"{activities_latest_heart_value3}," |
565 | | - + f"{activities_latest_heart_value2}," |
566 | | - + f"{activities_latest_heart_value1}," |
567 | | - + f"{activities_latest_heart_value0}" |
568 | | - ) |
569 | | - else: |
570 | | - print("Waiting for latest 15 values sync...") |
571 | | - print("Not enough values for today to display yet.") |
572 | | - print("No display from midnight to 00:15") |
573 | | - |
574 | | - except KeyError as keyerror: |
575 | | - print(f"Key Error: {keyerror}") |
576 | | - print( |
577 | | - "Too Many Requests, " |
578 | | - + "Expired token, " |
579 | | - + "invalid permission, or " |
580 | | - + "(key:value) pair error." |
581 | | - ) |
582 | | - continue |
583 | | - |
584 | | - print("Board Uptime: ", time_calc(time.monotonic())) # Board Up-Time seconds |
585 | | - print("\nFinished!") |
586 | | - print("Next Update in: ", time_calc(sleep_time)) |
587 | | - print("===============================") |
588 | | - |
589 | | - except (ValueError, RuntimeError) as e: |
590 | | - print("Failed to get data, retrying\n", e) |
591 | | - time.sleep(60) |
592 | | - continue |
593 | | - time.sleep(sleep_time) |
0 commit comments