|
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