From 1def0b66bdfac97bb37454f9afc7bdc28b4cd14f Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Sun, 24 Mar 2024 00:26:34 -0400 Subject: [PATCH 1/5] Update YouTube API Example with Connection Manager --- .../expanded/requests_wifi_api_youtube.py | 165 +++++++++--------- 1 file changed, 81 insertions(+), 84 deletions(-) diff --git a/examples/wifi/expanded/requests_wifi_api_youtube.py b/examples/wifi/expanded/requests_wifi_api_youtube.py index e9bc6a2..5215b10 100644 --- a/examples/wifi/expanded/requests_wifi_api_youtube.py +++ b/examples/wifi/expanded/requests_wifi_api_youtube.py @@ -1,123 +1,120 @@ -# SPDX-FileCopyrightText: 2022 DJDevon3 for Adafruit Industries +# SPDX-FileCopyrightText: 2024 DJDevon3 # SPDX-License-Identifier: MIT -# Coded for Circuit Python 8.0 -"""DJDevon3 Adafruit Feather ESP32-S2 YouTube_API_Example""" -import gc -import json +# Coded for Circuit Python 8.2.x +"""YouTube API Subscriber Count Example""" +# pylint: disable=import-error + import os -import ssl import time -import socketpool +import adafruit_connection_manager import wifi import adafruit_requests -# Ensure these are uncommented and in secrets.py or .env -# "YT_username": "Your YouTube Username", -# "YT_token" : "Your long API developer token", - -# Initialize WiFi Pool (There can be only 1 pool & top of script) -pool = socketpool.SocketPool(wifi.radio) +# Initalize Wifi, Socket Pool, Request Session +pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio) +requests = adafruit_requests.Session(pool, ssl_context) -# Time between API refreshes +# API Polling Rate # 900 = 15 mins, 1800 = 30 mins, 3600 = 1 hour -sleep_time = 900 +SLEEP_TIME = 900 + +# Set debug to True for full JSON response. +# WARNING: Will show credentials +DEBUG = False + +# Ensure these are uncommented and in settings.toml +# YOUTUBE_USERNAME = "Your YouTube Username", +# YOUTUBE_TOKEN = "Your long API developer token", # Get WiFi details, ensure these are setup in settings.toml ssid = os.getenv("CIRCUITPY_WIFI_SSID") password = os.getenv("CIRCUITPY_WIFI_PASSWORD") -yt_username = os.getenv("YT_username") -yt_token = os.getenv("YT_token") - - -if sleep_time < 60: - sleep_time_conversion = "seconds" - sleep_int = sleep_time -elif 60 <= sleep_time < 3600: - sleep_int = sleep_time / 60 - sleep_time_conversion = "minutes" -elif 3600 <= sleep_time < 86400: - sleep_int = sleep_time / 60 / 60 - sleep_time_conversion = "hours" -else: - sleep_int = sleep_time / 60 / 60 / 24 - sleep_time_conversion = "days" +# Requires Steam Developer API key +YT_USERNAME = os.getenv("YOUTUBE_USERNAME") +YT_TOKEN = os.getenv("YOUTUBE_TOKEN") + + +def time_calc(input_time): + """Converts seconds to minutes/hours/days""" + if input_time < 60: + return f"{input_time:.0f} seconds" + if input_time < 3600: + return f"{input_time / 60:.0f} minutes" + if input_time < 86400: + return f"{input_time / 60 / 60:.0f} hours" + return f"{input_time / 60 / 60 / 24:.1f} days" + # https://youtube.googleapis.com/youtube/v3/channels?part=statistics&forUsername=[YOUR_USERNAME]&key=[YOUR_API_KEY] -YT_SOURCE = ( - "https://youtube.googleapis.com/youtube/v3/channels?" - + "part=statistics" - + "&forUsername=" - + yt_username +YOUTUBE_SOURCE = ( + "https://youtube.googleapis.com/youtube/v3/channels?part=statistics&forUsername=" + + str(YT_USERNAME) + "&key=" - + yt_token + + str(YT_TOKEN) ) -# Connect to Wi-Fi -print("\n===============================") -print("Connecting to WiFi...") -requests = adafruit_requests.Session(pool, ssl.create_default_context()) -while not wifi.radio.ipv4_address: - try: - wifi.radio.connect(ssid, password) - except ConnectionError as e: - print("Connection Error:", e) - print("Retrying in 10 seconds") - time.sleep(10) - gc.collect() -print("Connected!\n") - while True: + # Connect to Wi-Fi + print("\nConnecting to WiFi...") + while not wifi.radio.ipv4_address: + try: + wifi.radio.connect(ssid, password) + except ConnectionError as e: + print("❌ Connection Error:", e) + print("Retrying in 10 seconds") + print("✅ Wifi!") try: - print("Attempting to GET YouTube Stats!") # ---------------------------------- - debug_request = False # Set true to see full request - if debug_request: - print("Full API GET URL: ", YT_SOURCE) - print("===============================") + print(" | Attempting to GET YouTube JSON...") try: - response = requests.get(YT_SOURCE).json() + youtube_response = requests.get(url=YOUTUBE_SOURCE) + youtube_json = youtube_response.json() except ConnectionError as e: print("Connection Error:", e) print("Retrying in 10 seconds") + print(" | ✅ YouTube JSON!") + + if DEBUG: + print(f" | Full API GET URL: {YOUTUBE_SOURCE}") + print(f" | Full API Dump: {youtube_json}") - # Print Full JSON to Serial - debug_response = False # Set true to see full response - if debug_response: - dump_object = json.dumps(response) - print("JSON Dump: ", dump_object) + # Key:Value RESPONSES + if "pageInfo" in youtube_json: + totalResults = youtube_json["pageInfo"]["totalResults"] + print(f" | | Matching Results: {totalResults}") - # Print to Serial - yt_debug_keys = True # Set to True to print Serial data - if yt_debug_keys: - print("Matching Results: ", response["pageInfo"]["totalResults"]) + if "items" in youtube_json: + YT_request_kind = youtube_json["items"][0]["kind"] + print(f" | | Request Kind: {YT_request_kind}") - YT_request_kind = response["items"][0]["kind"] - print("Request Kind: ", YT_request_kind) + YT_channel_id = youtube_json["items"][0]["id"] + print(f" | | Channel ID: {YT_channel_id}") - YT_response_kind = response["kind"] - print("Response Kind: ", YT_response_kind) + YT_videoCount = youtube_json["items"][0]["statistics"]["videoCount"] + print(f" | | Videos: {YT_videoCount}") - YT_channel_id = response["items"][0]["id"] - print("Channel ID: ", YT_channel_id) + YT_viewCount = youtube_json["items"][0]["statistics"]["viewCount"] + print(f" | | Views: {YT_viewCount}") - YT_videoCount = response["items"][0]["statistics"]["videoCount"] - print("Videos: ", YT_videoCount) + YT_subsCount = youtube_json["items"][0]["statistics"]["subscriberCount"] + print(f" | | Subscribers: {YT_subsCount}") - YT_viewCount = response["items"][0]["statistics"]["viewCount"] - print("Views: ", YT_viewCount) + if "kind" in youtube_json: + YT_response_kind = youtube_json["kind"] + print(f" | | Response Kind: {YT_response_kind}") - YT_subsCount = response["items"][0]["statistics"]["subscriberCount"] - print("Subscribers: ", YT_subsCount) - print("Monotonic: ", time.monotonic()) + youtube_response.close() + print("✂️ Disconnected from YouTube API") print("\nFinished!") - print("Next Update in %s %s" % (int(sleep_int), sleep_time_conversion)) + print(f"Board Uptime: {time_calc(time.monotonic())}") + print(f"Next Update: {time_calc(SLEEP_TIME)}") print("===============================") - gc.collect() except (ValueError, RuntimeError) as e: - print("Failed to get data, retrying\n", e) + print(f"Failed to get data, retrying\n {e}") time.sleep(60) - continue - time.sleep(sleep_time) + break + time.sleep(SLEEP_TIME) From fdc0da738f777754a4bc8721c6ab76048bbc6a5b Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Sun, 24 Mar 2024 00:51:30 -0400 Subject: [PATCH 2/5] added developer dashboard url comment --- examples/wifi/expanded/requests_wifi_api_youtube.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/wifi/expanded/requests_wifi_api_youtube.py b/examples/wifi/expanded/requests_wifi_api_youtube.py index 5215b10..85bda0d 100644 --- a/examples/wifi/expanded/requests_wifi_api_youtube.py +++ b/examples/wifi/expanded/requests_wifi_api_youtube.py @@ -32,7 +32,8 @@ # Get WiFi details, ensure these are setup in settings.toml ssid = os.getenv("CIRCUITPY_WIFI_SSID") password = os.getenv("CIRCUITPY_WIFI_PASSWORD") -# Requires Steam Developer API key +# Requires YouTube/Google API key +# https://console.cloud.google.com/apis/dashboard YT_USERNAME = os.getenv("YOUTUBE_USERNAME") YT_TOKEN = os.getenv("YOUTUBE_TOKEN") From 750317a6fbb0113731e5d7389b7a0a5e44f85bdf Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Mon, 25 Mar 2024 02:27:05 -0400 Subject: [PATCH 3/5] minor updates Using newer time_calc function, relabeled some variables, fixed os.getenv defaults for CIRCUITPY_ variables. made sleep_time shouty. board uptime displays time_calc human readable instead of monotonic seconds. --- .../requests_wifi_api_rocketlaunch_live.py | 66 +++++++++++-------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/examples/wifi/expanded/requests_wifi_api_rocketlaunch_live.py b/examples/wifi/expanded/requests_wifi_api_rocketlaunch_live.py index dc497a4..7cf9d61 100644 --- a/examples/wifi/expanded/requests_wifi_api_rocketlaunch_live.py +++ b/examples/wifi/expanded/requests_wifi_api_rocketlaunch_live.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2024 DJDevon3 # SPDX-License-Identifier: MIT -# Coded for Circuit Python 8.2.x +# Coded for Circuit Python 9.0 """RocketLaunch.Live API Example""" import os @@ -13,31 +13,22 @@ # Time between API refreshes # 900 = 15 mins, 1800 = 30 mins, 3600 = 1 hour -sleep_time = 43200 +SLEEP_TIME = 43200 # Get WiFi details, ensure these are setup in settings.toml -ssid = os.getenv("WIFI_SSID") -password = os.getenv("WIFI_PASSWORD") +ssid = os.getenv("CIRCUITPY_WIFI_SSID") +password = os.getenv("CIRCUITPY_WIFI_PASSWORD") -# Converts seconds in minutes/hours/days def time_calc(input_time): + """Converts seconds to minutes/hours/days""" if input_time < 60: - sleep_int = input_time - time_output = f"{sleep_int:.0f} seconds" - elif 60 <= input_time < 3600: - sleep_int = input_time / 60 - time_output = f"{sleep_int:.0f} minutes" - elif 3600 <= input_time < 86400: - sleep_int = input_time / 60 / 60 - time_output = f"{sleep_int:.0f} hours" - elif 86400 <= input_time < 432000: - sleep_int = input_time / 60 / 60 / 24 - time_output = f"{sleep_int:.1f} days" - else: # if > 5 days convert float to int & display whole days - sleep_int = input_time / 60 / 60 / 24 - time_output = f"{sleep_int:.0f} days" - return time_output + return f"{input_time:.0f} seconds" + if input_time < 3600: + return f"{input_time / 60:.0f} minutes" + if input_time < 86400: + return f"{input_time / 60 / 60:.0f} hours" + return f"{input_time / 60 / 60 / 24:.1f} days" # Publicly available data no header required @@ -81,7 +72,7 @@ def time_calc(input_time): # JSON Endpoints RLFN = str(rocketlaunch_json["result"][0]["name"]) RLWO = str(rocketlaunch_json["result"][0]["win_open"]) - TMINUS = str(rocketlaunch_json["result"][0]["t0"]) + TZERO = str(rocketlaunch_json["result"][0]["t0"]) RLWC = str(rocketlaunch_json["result"][0]["win_close"]) RLP = str(rocketlaunch_json["result"][0]["provider"]["name"]) RLVN = str(rocketlaunch_json["result"][0]["vehicle"]["name"]) @@ -100,10 +91,26 @@ def time_calc(input_time): print(f" | | Provider: {RLP}") if RLVN != "None": print(f" | | Vehicle: {RLVN}") - if RLWO != "None": - print(f" | | Window: {RLWO} to {RLWC}") - elif TMINUS != "None": - print(f" | | Window: {TMINUS} to {RLWC}") + + # Launch time can sometimes be Window Open to Close, T-Zero, or weird combination. + # Should obviously be standardized but they're not input that way. + # Have to account for every combination of 3 conditions. + # T-Zero Launch Time Conditionals + if RLWO == "None" and TZERO != "None" and RLWC != "None": + print(f" | | Window: {TZERO} | {RLWC}") + elif RLWO != "None" and TZERO != "None" and RLWC == "None": + print(f" | | Window: {RLWO} | {TZERO}") + elif RLWO != "None" and TZERO == "None" and RLWC != "None": + print(f" | | Window: {RLWO} | {RLWC}") + elif RLWO != "None" and TZERO != "None" and RLWC != "None": + print(f" | | Window: {RLWO} | {TZERO} | {RLWC}") + elif RLWO == "None" and TZERO != "None" and RLWC == "None": + print(f" | | Window: {TZERO}") + elif RLWO != "None" and TZERO == "None" and RLWC == "None": + print(f" | | Window: {RLWO}") + elif RLWO == "None" and TZERO == "None" and RLWC != "None": + print(f" | | Window: {RLWC}") + if RLLS != "None": print(f" | | Site: {RLLS}") if RLPN != "None": @@ -113,13 +120,16 @@ def time_calc(input_time): if RLM != "None": print(f" | | Mission: {RLM}") + rocketlaunch_response.close() + print("✂️ Disconnected from RocketLaunch.Live API") + print("\nFinished!") - print("Board Uptime: ", time.monotonic()) - print("Next Update in: ", time_calc(sleep_time)) + print(f"Board Uptime: {time_calc(time.monotonic())}") + print(f"Next Update: {time_calc(SLEEP_TIME)}") print("===============================") except (ValueError, RuntimeError) as e: print("Failed to get data, retrying\n", e) time.sleep(60) break - time.sleep(sleep_time) + time.sleep(SLEEP_TIME) From 408e1bf9ca1eba8300a3cf061651c086d8538804 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 1 Apr 2024 14:24:44 -0500 Subject: [PATCH 4/5] remove unneeded pylint disable in examples --- examples/wifi/expanded/requests_wifi_api_youtube.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/wifi/expanded/requests_wifi_api_youtube.py b/examples/wifi/expanded/requests_wifi_api_youtube.py index 85bda0d..c69c146 100644 --- a/examples/wifi/expanded/requests_wifi_api_youtube.py +++ b/examples/wifi/expanded/requests_wifi_api_youtube.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: MIT # Coded for Circuit Python 8.2.x """YouTube API Subscriber Count Example""" -# pylint: disable=import-error import os import time From 94db22c36fd3a45b2d165a00787958a6a9250160 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 1 Apr 2024 14:24:56 -0500 Subject: [PATCH 5/5] remove unneeded pylint disable in examples --- .../expanded/requests_wifi_adafruit_discord_active_online.py | 1 - examples/wifi/expanded/requests_wifi_api_discord.py | 1 - examples/wifi/expanded/requests_wifi_api_fitbit.py | 2 +- examples/wifi/expanded/requests_wifi_api_github.py | 1 - examples/wifi/expanded/requests_wifi_api_mastodon.py | 1 - examples/wifi/expanded/requests_wifi_api_premiereleague.py | 1 - examples/wifi/expanded/requests_wifi_api_steam.py | 1 - examples/wifi/expanded/requests_wifi_api_twitch.py | 1 - 8 files changed, 1 insertion(+), 8 deletions(-) diff --git a/examples/wifi/expanded/requests_wifi_adafruit_discord_active_online.py b/examples/wifi/expanded/requests_wifi_adafruit_discord_active_online.py index dec0e6e..3ebcaf4 100644 --- a/examples/wifi/expanded/requests_wifi_adafruit_discord_active_online.py +++ b/examples/wifi/expanded/requests_wifi_adafruit_discord_active_online.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: MIT # Coded for Circuit Python 8.2.x """Discord Active Online Shields.IO Example""" -# pylint: disable=import-error import os import time diff --git a/examples/wifi/expanded/requests_wifi_api_discord.py b/examples/wifi/expanded/requests_wifi_api_discord.py index 83b8f8f..08ad16c 100644 --- a/examples/wifi/expanded/requests_wifi_api_discord.py +++ b/examples/wifi/expanded/requests_wifi_api_discord.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: MIT # Coded for Circuit Python 8.2.x """Discord Web Scrape Example""" -# pylint: disable=import-error import os import time diff --git a/examples/wifi/expanded/requests_wifi_api_fitbit.py b/examples/wifi/expanded/requests_wifi_api_fitbit.py index 802995c..206a56d 100644 --- a/examples/wifi/expanded/requests_wifi_api_fitbit.py +++ b/examples/wifi/expanded/requests_wifi_api_fitbit.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: MIT # Coded for Circuit Python 8.2.x """Fitbit API Example""" -# pylint: disable=import-error, disable=no-member +# pylint: disable=no-member import os import time diff --git a/examples/wifi/expanded/requests_wifi_api_github.py b/examples/wifi/expanded/requests_wifi_api_github.py index 6fbe8fe..e045299 100644 --- a/examples/wifi/expanded/requests_wifi_api_github.py +++ b/examples/wifi/expanded/requests_wifi_api_github.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: MIT # Coded for Circuit Python 8.2.x """Github API Example""" -# pylint: disable=import-error import os import time diff --git a/examples/wifi/expanded/requests_wifi_api_mastodon.py b/examples/wifi/expanded/requests_wifi_api_mastodon.py index f6bce9f..fd4266e 100644 --- a/examples/wifi/expanded/requests_wifi_api_mastodon.py +++ b/examples/wifi/expanded/requests_wifi_api_mastodon.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: MIT # Coded for Circuit Python 8.2.x """Mastodon API Example""" -# pylint: disable=import-error import os import time diff --git a/examples/wifi/expanded/requests_wifi_api_premiereleague.py b/examples/wifi/expanded/requests_wifi_api_premiereleague.py index 0a2e607..88524e9 100644 --- a/examples/wifi/expanded/requests_wifi_api_premiereleague.py +++ b/examples/wifi/expanded/requests_wifi_api_premiereleague.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: MIT # Coded for Circuit Python 8.2.x """Premiere League Total Players API Example""" -# pylint: disable=import-error import os import time diff --git a/examples/wifi/expanded/requests_wifi_api_steam.py b/examples/wifi/expanded/requests_wifi_api_steam.py index a05d6c7..fb1f7bd 100644 --- a/examples/wifi/expanded/requests_wifi_api_steam.py +++ b/examples/wifi/expanded/requests_wifi_api_steam.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: MIT # Coded for Circuit Python 8.2.x """Steam API Get Owned Games Example""" -# pylint: disable=import-error import os import time diff --git a/examples/wifi/expanded/requests_wifi_api_twitch.py b/examples/wifi/expanded/requests_wifi_api_twitch.py index 8e15098..1f02e5c 100644 --- a/examples/wifi/expanded/requests_wifi_api_twitch.py +++ b/examples/wifi/expanded/requests_wifi_api_twitch.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: MIT # Coded for Circuit Python 8.2.x """Twitch API Example""" -# pylint: disable=import-error import os import time