228 lines
6.5 KiB
Python
228 lines
6.5 KiB
Python
# SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
# ON AIR sign for YouTube livestreaming
|
|
# Runs on Airlift Metro M4 with 64x32 RGB Matrix display & shield
|
|
|
|
from os import getenv
|
|
import time
|
|
import board
|
|
import displayio
|
|
import adafruit_display_text.label
|
|
from adafruit_display_shapes.rect import Rect
|
|
from adafruit_display_shapes.polygon import Polygon
|
|
from adafruit_bitmap_font import bitmap_font
|
|
from adafruit_matrixportal.network import Network
|
|
from adafruit_matrixportal.matrix import Matrix
|
|
|
|
# Get WiFi details, ensure these are setup in settings.toml
|
|
ssid = getenv("CIRCUITPY_WIFI_SSID")
|
|
password = getenv("CIRCUITPY_WIFI_PASSWORD")
|
|
|
|
if None in [ssid, password]:
|
|
raise RuntimeError(
|
|
"WiFi settings are kept in settings.toml, "
|
|
"please add them there. The settings file must contain "
|
|
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
|
|
"at a minimum."
|
|
)
|
|
|
|
# Set up where we'll be fetching data from
|
|
# Adafruit YouTube channel:
|
|
CHANNEL_ID = (
|
|
"UCpOlOeQjj7EsVnDh3zuCgsA" # this isn't a secret but you have to look it up
|
|
)
|
|
|
|
DATA_SOURCE = (
|
|
"https://www.googleapis.com/youtube/v3/search?part=snippet&channelId="
|
|
+ CHANNEL_ID
|
|
+ "&type=video&eventType=live&key="
|
|
+ getenv("youtube_token")
|
|
)
|
|
DATA_LOCATION1 = ["items"]
|
|
|
|
# Number of seconds between checking, if this is too quick the query quota will run out
|
|
UPDATE_DELAY = 300
|
|
|
|
# Times are in 24-hour format for simplification
|
|
OPERATING_TIME_START = "12:00" # what hour to start checking
|
|
OPERATING_TIME_END = "19:00" # what hour to stop checking
|
|
|
|
# --- Display setup ---
|
|
matrix = Matrix()
|
|
display = matrix.display
|
|
network = Network(status_neopixel=board.NEOPIXEL, debug=False)
|
|
|
|
# --- Drawing setup ---
|
|
# Create a Group
|
|
group = displayio.Group()
|
|
# Create a bitmap object
|
|
bitmap = displayio.Bitmap(64, 32, 2) # width, height, bit depth
|
|
# Create a color palette
|
|
color = displayio.Palette(4)
|
|
color[0] = 0x000000 # black
|
|
color[1] = 0xFF0000 # red
|
|
color[2] = 0x444444 # dim white
|
|
color[3] = 0xDD8000 # gold
|
|
# Create a TileGrid using the Bitmap and Palette
|
|
tile_grid = displayio.TileGrid(bitmap, pixel_shader=color)
|
|
# Add the TileGrid to the Group
|
|
group.append(tile_grid)
|
|
|
|
# draw the frame for startup
|
|
rect1 = Rect(0, 0, 2, 32, fill=color[2])
|
|
rect2 = Rect(62, 0, 2, 32, fill=color[2])
|
|
rect3 = Rect(2, 0, 9, 2, fill=color[0])
|
|
rect4 = Rect(53, 0, 9, 2, fill=color[0])
|
|
rect5 = Rect(2, 30, 12, 2, fill=color[0])
|
|
rect6 = Rect(50, 30, 12, 2, fill=color[0])
|
|
|
|
group.append(rect1)
|
|
group.append(rect2)
|
|
group.append(rect3)
|
|
group.append(rect4)
|
|
group.append(rect5)
|
|
group.append(rect6)
|
|
|
|
|
|
def redraw_frame(): # to adjust spacing at bottom later
|
|
rect3.fill = color[2]
|
|
rect4.fill = color[2]
|
|
rect5.fill = color[2]
|
|
rect6.fill = color[2]
|
|
|
|
|
|
# draw the wings w polygon shapes
|
|
wing_polys = []
|
|
|
|
wing_polys.append(Polygon([(3, 3), (9, 3), (9, 4), (4, 4)], outline=color[1]))
|
|
wing_polys.append(Polygon([(5, 6), (9, 6), (9, 7), (6, 7)], outline=color[1]))
|
|
wing_polys.append(Polygon([(7, 9), (9, 9), (9, 10), (8, 10)], outline=color[1]))
|
|
wing_polys.append(Polygon([(54, 3), (60, 3), (59, 4), (54, 4)], outline=color[1]))
|
|
wing_polys.append(Polygon([(54, 6), (58, 6), (57, 7), (54, 7)], outline=color[1]))
|
|
wing_polys.append(Polygon([(54, 9), (56, 9), (55, 10), (54, 10)], outline=color[1]))
|
|
|
|
for wing_poly in wing_polys:
|
|
group.append(wing_poly)
|
|
|
|
|
|
def redraw_wings(index): # to change colors
|
|
for wing in wing_polys:
|
|
wing.outline = color[index]
|
|
|
|
|
|
# --- Content Setup ---
|
|
deco_font = bitmap_font.load_font("/BellotaText-Bold-21.bdf")
|
|
|
|
# Create two lines of text. Besides changing the text, you can also
|
|
# customize the color and font (using Adafruit_CircuitPython_Bitmap_Font).
|
|
|
|
# text positions
|
|
on_x = 15
|
|
on_y = 9
|
|
off_x = 12
|
|
off_y = 9
|
|
air_x = 15
|
|
air_y = 25
|
|
|
|
|
|
text_line1 = adafruit_display_text.label.Label(deco_font, color=color[3], text="OFF")
|
|
text_line1.x = off_x
|
|
text_line1.y = off_y
|
|
|
|
text_line2 = adafruit_display_text.label.Label(deco_font, color=color[1], text="AIR")
|
|
text_line2.x = air_x
|
|
text_line2.y = air_y
|
|
|
|
# Put each line of text into the Group
|
|
group.append(text_line1)
|
|
group.append(text_line2)
|
|
|
|
|
|
def startup_text():
|
|
text_line1.text = "ADA"
|
|
text_line1.x = 10
|
|
text_line1.color = color[2]
|
|
text_line2.text = "FRUIT"
|
|
text_line2.x = 2
|
|
text_line2.color = color[2]
|
|
redraw_wings(0)
|
|
display.root_group = group
|
|
|
|
|
|
startup_text() # display the startup text
|
|
|
|
|
|
def update_text(state):
|
|
if state: # if switch is on, text is "ON" at startup
|
|
text_line1.text = "ON"
|
|
text_line1.x = on_x
|
|
text_line1.color = color[1]
|
|
text_line2.text = "AIR"
|
|
text_line2.x = air_x
|
|
text_line2.color = color[1]
|
|
redraw_wings(1)
|
|
redraw_frame()
|
|
display.root_group = group
|
|
else: # else, text if "OFF" at startup
|
|
text_line1.text = "OFF"
|
|
text_line1.x = off_x
|
|
text_line1.color = color[3]
|
|
text_line2.text = "AIR"
|
|
text_line2.x = air_x
|
|
text_line2.color = color[3]
|
|
redraw_wings(3)
|
|
redraw_frame()
|
|
display.root_group = group
|
|
|
|
|
|
def get_status():
|
|
"""
|
|
Get the status whether we are on/off air within operating hours
|
|
If outside of hours, return False
|
|
"""
|
|
# Get the time values we need
|
|
now = time.localtime()
|
|
start_hour, start_minute = OPERATING_TIME_START.split(":")
|
|
end_hour, end_minute = OPERATING_TIME_END.split(":")
|
|
|
|
# Convert time into a float for easy calculations
|
|
start_time = int(start_hour) + (int(start_minute) / 60)
|
|
end_time = int(end_hour) + (int(end_minute) / 60)
|
|
current_time = now[3] + (now[4] / 60)
|
|
|
|
if start_time <= current_time < end_time:
|
|
try:
|
|
on_air = network.fetch_data(DATA_SOURCE, json_path=(DATA_LOCATION1,))
|
|
if len(on_air) > 0:
|
|
return True
|
|
except RuntimeError:
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
# Synchronize Board's clock to Internet
|
|
network.get_local_time()
|
|
mode_state = get_status()
|
|
update_text(mode_state)
|
|
last_check = None
|
|
|
|
|
|
while True:
|
|
if last_check is None or time.monotonic() > last_check + UPDATE_DELAY:
|
|
try:
|
|
status = get_status()
|
|
if status:
|
|
if mode_state == 0: # state has changed, toggle it
|
|
update_text(1)
|
|
mode_state = 1
|
|
else:
|
|
if mode_state == 1:
|
|
update_text(0)
|
|
mode_state = 0
|
|
print("On Air:", status)
|
|
last_check = time.monotonic()
|
|
except RuntimeError as e:
|
|
print("Some error occured, retrying! -", e)
|