Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Queue-Times API Example #182

Merged
merged 4 commits into from
Apr 28, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions examples/wifi/expanded/requests_wifi_api_queuetimes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# SPDX-FileCopyrightText: 2024 DJDevon3
# SPDX-License-Identifier: MIT
# Coded for Circuit Python 9.x
"""Queue-Times.com API Example"""

import os
import time

import adafruit_connection_manager
import wifi

import adafruit_requests

# 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
# 900 = 15 mins, 1800 = 30 mins, 3600 = 1 hour
SLEEP_TIME = 300

# Get WiFi details, ensure these are setup in settings.toml
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")

# Publicly Open API (no credentials required)
QTIMES_SOURCE = "https://queue-times.com/parks/16/queue_times.json"


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"


qtimes_json = {}
while True:
now = time.monotonic()
# Connect to Wi-Fi
print("\n===============================")
print("Connecting 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 Queue-Times JSON!")
try:
qtimes_response = requests.get(url=QTIMES_SOURCE)
qtimes_json = qtimes_response.json()
except ConnectionError as e:
print("Connection Error:", e)
print("Retrying in 10 seconds")
print(" | ✅ Queue-Times JSON!")

DEBUG_QTIMES = False
if DEBUG_QTIMES:
print("Full API GET URL: ", QTIMES_SOURCE)
print(qtimes_json)
qtimes_response.close()
print("✂️ Disconnected from Queue-Times API")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
try:
qtimes_response = requests.get(url=QTIMES_SOURCE)
qtimes_json = qtimes_response.json()
except ConnectionError as e:
print("Connection Error:", e)
print("Retrying in 10 seconds")
print(" | ✅ Queue-Times JSON!")
DEBUG_QTIMES = False
if DEBUG_QTIMES:
print("Full API GET URL: ", QTIMES_SOURCE)
print(qtimes_json)
qtimes_response.close()
print("✂️ Disconnected from Queue-Times API")
try:
with requests.get(url=QTIMES_SOURCE) as qtimes_response:
qtimes_json = qtimes_response.json()
except ConnectionError as e:
print("Connection Error:", e)
print("Retrying in 10 seconds")
print(" | ✅ Queue-Times JSON!")
DEBUG_QTIMES = False
if DEBUG_QTIMES:
print("Full API GET URL: ", QTIMES_SOURCE)
print(qtimes_json)


print("\nFinished!")
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

# Loop infinitely until its time to re-poll
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This extra loop that is printing the results over and over again until it's time to fetch new data confused me.

At first I thought it was actually retching new data all the time and worried about rate limit problem with the server, but I did eventually realize it's not actually fetching, just re-printing the same data in a loop while it waits for the next time to fetch.

I'm in favor of removing that loop and just printing the results once. By printing them over and over it feels like it's implying to the user that there is new data to see when it's actually just printing the same data again. It also makes it a little hard to hone in on a specific ride if they are interested in one. (Though they of course could modify it to print a single one, granted.)

Copy link
Contributor Author

@DJDevon3 DJDevon3 Apr 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FoamyGuy Yes, that is specifically the way they requested it to behave.

I suppose printing a "last updated" timestamp would make sense but would take up precious screen real estate. It makes less sense on a huge 3.5" TFT.

The project intention is for a portable SSD1306 with a lipo battery on a watch band. In the real script it will attempt to reconnect (via a tethered phone hotspot) every 5 minutes. Perhaps I could improve upon the code comments to specify that's the intention of this particular example?

If you gave one device to each kid and it only worked within range of the hotspot it could ensure that your kids will want to stay close enough to ensure the device works so they get ride updates too. It's a brilliant incentive for your kids to want to stay close to you in a theme park.

Maybe change the text color after an update so that every new update is unmistakably new? That would serve as an indicator of new content without taking up any screen real estate but a display is not included in this example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am still in favor of removing the looping behavior from the example here. That sounds like a neat project and they can definitely still use it that way if they'd like. I could also see a world where someone might want to "favorite" certain rides to get information about them more prominently or even some alerts based on the data returned.

I believe that in it's usage as an example here the continued printing of the same data over and over makes it more confusing because it's difficult to tell what is happening and when there is actually new data. None of the rest of the examples in this repo behave that way, they're all either "single shot" fetching once and ending, or repeating in a loop that fetches every X minutes or seconds and prints the current data then waits until it's time to run again. I believe it's best to have this example match that behavior and only print the data once after it's been fetched.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FoamyGuy sounds good. I have the example that the person wanted in my personal repo, and linked them there, and they already have that one. Turning it into a single use "code done running" sounds good to me.

if time.monotonic() - now <= SLEEP_TIME:
for land in qtimes_json["lands"]:
qtimes_lands = str(land["name"])
print(f" | | Lands: {qtimes_lands}")
time.sleep(1)

# Loop through each ride in the land
for ride in land["rides"]:
qtimes_rides = str(ride["name"])
qtimes_queuetime = str(ride["wait_time"])
qtimes_isopen = str(ride["is_open"])

print(f" | | Ride: {qtimes_rides}")
print(f" | | Queue Time: {qtimes_queuetime} Minutes")
if qtimes_isopen == "False":
print(" | | Status: Closed")
elif qtimes_isopen == "True":
print(" | | Status: Open")
else:
print(" | | Status: Unknown")

time.sleep(1) # delay between list items
else: # When its time to poll, break to top of while True loop.
break
Loading