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

Adding apiimage applet #2712

Merged
merged 52 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
31bb231
Adding apiimage applet
MichaelYagi Sep 21, 2024
6f5197f
Adding apiimage applet
MichaelYagi Sep 21, 2024
1952157
Adding apiimage applet
MichaelYagi Sep 21, 2024
1c42b20
Adding apiimage applet
MichaelYagi Sep 21, 2024
4657194
Adding apiimage applet
MichaelYagi Sep 21, 2024
781efc0
I have read the CLA Document and I hereby sign the CLA
MichaelYagi Sep 21, 2024
4f2c01d
I have read the CLA Document and I hereby sign the CLA
MichaelYagi Sep 21, 2024
3251edd
I have read the CLA Document and I hereby sign the CLA
MichaelYagi Sep 21, 2024
baef677
Updated TTL for API image app
MichaelYagi Sep 21, 2024
681b7c9
Updated TTL for API image app
MichaelYagi Sep 21, 2024
20d132e
Updated caching in app
MichaelYagi Sep 22, 2024
2f9a304
Turn off debug flag
MichaelYagi Sep 22, 2024
bfcef23
Fixed header array bug
MichaelYagi Sep 22, 2024
248fe58
Turned off debug mode
MichaelYagi Sep 22, 2024
145b4e6
Added debug toggle
MichaelYagi Sep 22, 2024
5fddd10
Fixed infinite loop bug
MichaelYagi Sep 22, 2024
7403f46
Added support for image types
MichaelYagi Sep 22, 2024
eff3615
Check if base URL needed
MichaelYagi Sep 22, 2024
3b32f96
Message cleanup
MichaelYagi Sep 22, 2024
6c3a4df
Added fit to screen option
MichaelYagi Sep 22, 2024
ccf1d8a
Reordered schema
MichaelYagi Sep 22, 2024
45f4bbd
Removed direct imageURL support
MichaelYagi Sep 23, 2024
a4c2f04
Readded direct image URLs
MichaelYagi Sep 23, 2024
d797183
Checking headers for content type
MichaelYagi Sep 23, 2024
490fa77
Added more checks for valid paths
MichaelYagi Sep 23, 2024
829adfb
Added more checks for valid paths
MichaelYagi Sep 23, 2024
9c52629
Cleaning up messages
MichaelYagi Sep 23, 2024
f259908
Better way of checking headers
MichaelYagi Sep 23, 2024
399e9ae
Clarified debug messages
MichaelYagi Sep 23, 2024
cee5841
Clarified debug messages
MichaelYagi Sep 23, 2024
392b421
Renamed api_headers variable to request_headers for clarity
MichaelYagi Sep 23, 2024
3df513d
Don't print the JSON string
MichaelYagi Sep 23, 2024
9d77fd0
Updated debug message
MichaelYagi Sep 23, 2024
c383410
Updated debug message
MichaelYagi Sep 23, 2024
b476bd5
Added content type debug message
MichaelYagi Sep 23, 2024
63236c7
Added convenience function when in debug mode
MichaelYagi Sep 23, 2024
5055a64
Renamed config
MichaelYagi Sep 23, 2024
57e48fc
Truncated json in debug output
MichaelYagi Sep 24, 2024
1723e4a
Updated schema
MichaelYagi Sep 24, 2024
b228bbd
Refactoring
MichaelYagi Sep 25, 2024
e10bba8
Clarified error message
MichaelYagi Sep 25, 2024
d36dcb1
Clarified error message
MichaelYagi Sep 25, 2024
d219d91
Clarified error message
MichaelYagi Sep 25, 2024
20331a7
Additional checks for response path lists
MichaelYagi Sep 25, 2024
84a9630
Simplified path checks
MichaelYagi Sep 25, 2024
df765a9
Code cleanup
MichaelYagi Sep 25, 2024
3167ab4
Moved TTL to get
MichaelYagi Sep 25, 2024
cd09b81
Merge branch 'tidbyt:main' into main
MichaelYagi Sep 25, 2024
8cd3da2
Added output on cache hit
MichaelYagi Sep 26, 2024
24e30c1
Merge branch 'main' of github.com:MichaelYagi/community
MichaelYagi Sep 26, 2024
39b85a2
Removed cache module
MichaelYagi Sep 26, 2024
ced8fec
Merge branch 'tidbyt:main' into main
MichaelYagi Sep 26, 2024
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
274 changes: 274 additions & 0 deletions apps/apiimage/api_image.star
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
"""
Applet: API image
Summary: API image display
Description: Display an image from an API endpoint.
Author: Michael Yagi
"""

load("encoding/json.star", "json")
load("http.star", "http")
load("render.star", "render")
load("schema.star", "schema")

def main(config):
base_url = config.str("base_url", "")
api_url = config.str("api_url", "")
response_path = config.get("response_path", "")
request_headers = config.get("request_headers", "")
debug_output = config.bool("debug_output", False)
fit_screen = config.bool("fit_screen", False)

if debug_output:
print("------------------------------")
print("CONFIG - api_url: " + api_url)
print("CONFIG - base_url: " + base_url)
print("CONFIG - response_path: " + response_path)
print("CONFIG - request_headers: " + request_headers)
print("CONFIG - debug_output: " + str(debug_output))
print("CONFIG - fit_screen: " + str(fit_screen))

return get_image(base_url, api_url, response_path, request_headers, debug_output, fit_screen)

def get_image(base_url, api_url, response_path, request_headers, debug_output, fit_screen):
failure = False
message = ""

if api_url == "":
failure = True
message = "API URL must not be blank"

if debug_output:
print(message)

else:
headerMap = {}
if request_headers != "" or request_headers != {}:
request_headers_array = request_headers.split(",")

for app_header in request_headers_array:
headerKeyValueArray = app_header.split(":")
if len(headerKeyValueArray) > 1:
headerMap[headerKeyValueArray[0].strip()] = headerKeyValueArray[1].strip()

output_body = get_data(api_url, debug_output, headerMap)

if output_body != None and type(output_body) == "string":
output = json.decode(output_body, None)
responsePathArray = []

if output_body != "":
if debug_output:
outputStr = str(output)
outputLen = len(outputStr)
if outputLen >= 200:
outputLen = 200

outputStr = outputStr[0:outputLen]
if outputLen >= 200:
outputStr = outputStr + "..."
print("Decoded JSON truncated: " + outputStr)
else:
print("Decoded JSON: " + outputStr)

if failure == False:
img = None

if output != None:
if response_path != "":
# Parse response path for JSON
if response_path != "":
responsePathArray = response_path

responsePathArray = responsePathArray.split(",")

for item in responsePathArray:
item = item.strip()
if item.isdigit():
item = int(item)

if debug_output:
print("path array item: " + str(item) + " - type " + str(type(output)))

if output != None and type(output) == "dict" and type(item) == "string" and output.get(item) != None:
output = output[item]
elif output != None and type(output) == "list" and type(item) == "int" and item <= len(output) - 1 and output[item] != None:
output = output[item]
else:
failure = True
message = "Response path invalid. " + str(item) + " does not exist"
if debug_output:
print("responsePathArray invalid. " + str(item) + " does not exist")
break

if debug_output:
print("Response content type JSON")

if type(output) == "string" and output.startswith("http") == False and (base_url == "" or base_url.startswith("http") == False):
failure = True
message = "Base URL required"
if debug_output:
print("Invalid URL. Requires a base_url")
elif type(output) == "string":
if output.startswith("http") == False and base_url != "":
url = base_url + output
else:
url = output

img = get_data(url, debug_output)

if debug_output:
print("Image URL: " + url)
else:
if message == "":
message = "Bad response path for JSON. Must point to an image URL."
if debug_output:
print(message)
failure = True
else:
message = "Missing response path for JSON"
if debug_output:
print(message)
failure = True

else:
if debug_output:
print("Response content type image")
img = output_body

if img != None:
imgRender = render.Image(
src = img,
height = 32,
)

if fit_screen == True:
imgRender = render.Image(
src = img,
width = 64,
)

return render.Root(
render.Row(
expanded = True,
main_align = "space_evenly",
cross_align = "center",
children = [imgRender],
),
)

else:
message = "Invalid image URL"
if debug_output:
print(message)
print(output)
failure = True
# return get_image(base_url, api_url, response_path, request_headers, debug_output)

else:
message = "Oops! Check URL and header values. URL must return JSON or image."
if debug_output:
print(message)
failure = True

if message == "":
message = "Could not get image"

row = render.Row(children = [])
if debug_output == True:
row = render.Row(
main_align = "space_evenly",
cross_align = "center",
children = [
render.WrappedText(content = message, font = "tom-thumb"),
],
)

return render.Root(
child = render.Box(
row,
),
)

def get_data(url, debug_output, headerMap = {}, ttl_seconds = 20):
if headerMap == {}:
res = http.get(url, ttl_seconds = ttl_seconds)
else:
res = http.get(url, headers = headerMap, ttl_seconds = ttl_seconds)

headers = res.headers
isValidContentType = False

headersStr = str(headers)
headersStr = headersStr.lower()
headers = json.decode(headersStr, None)
contentType = ""
if headers != None and headers.get("content-type") != None:
contentType = headers.get("content-type")

if contentType.find("json") != -1 or contentType.find("image") != -1:
isValidContentType = True

if debug_output:
print("isValidContentType for " + url + " content type " + contentType + ": " + str(isValidContentType))

if res.status_code != 200 or isValidContentType == False:
if debug_output:
print("status: " + str(res.status_code))
print("Requested url: " + str(url))
else:
data = res.body()

return data

return None

def get_schema():
return schema.Schema(
version = "1",
fields = [
schema.Text(
id = "api_url",
name = "API URL",
desc = "The API URL. Supports JSON or image types.",
icon = "",
default = "",
# default = "https://dog.ceo/api/breeds/image/random",
),
schema.Text(
id = "base_url",
name = "Base URL",
desc = "The base URL if response JSON contains relative paths.",
icon = "",
default = "",
),
schema.Text(
id = "response_path",
name = "JSON response path",
desc = "A comma separated path to the image URL in the response JSON. eg. `json_key_1, 2, json_key_to_image_url`",
icon = "",
default = "",
# default = "message",
),
schema.Text(
id = "request_headers",
name = "Request headers",
desc = "Comma separated key:value pairs to build the request headers. eg, `x-api-key:abc123,content-type:application/json`",
icon = "",
default = "",
),
schema.Toggle(
id = "fit_screen",
name = "Fit screen",
desc = "Fit image on screen.",
icon = "",
default = False,
),
schema.Toggle(
id = "debug_output",
name = "Toggle debug messages",
desc = "Toggle debug messages. Will display the messages on the display if enabled.",
icon = "",
default = False,
),
],
)
6 changes: 6 additions & 0 deletions apps/apiimage/manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
id: api-image
name: API Image
summary: Pull an image from an API
desc: Configure a URL, headers and the response path to the image to display.
author: MichaelYagi
Loading