Adds Lobster Vision app (viewer for timelapse camera recording site build)
parent
0c40c9ee1e
commit
d63360c4ae
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
|
@ -0,0 +1,286 @@
|
|||
"""View images from the EMF 2018 time-lapse camera
|
||||
"""
|
||||
___name___ = "Lobster Vision"
|
||||
___license___ = "MIT"
|
||||
___dependencies___ = ["app", "dialogs", "wifi", "buttons", "http", "ugfx_helper"]
|
||||
___categories___ = ["Other"]
|
||||
|
||||
import ugfx, wifi, dialogs, utime, ugfx_helper, buttons
|
||||
import gc
|
||||
from http import *
|
||||
from tilda import Buttons
|
||||
|
||||
IMAGE_PROXY = 'http://imageproxy.lobsterdev.com/api/'
|
||||
ACCESS_KEY = 'ZW1mMjAxODplbWYyMDE4'
|
||||
FULL_MONTHS = ['January', 'February', 'March', 'April', 'May', 'June',
|
||||
'July', 'August', 'September', 'October', 'November',
|
||||
'December']
|
||||
DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
|
||||
'Saturday', 'Sunday']
|
||||
|
||||
PHOTO_FILE = 'lobstervision/photo.gif'
|
||||
projects = []
|
||||
selectedProject = 0
|
||||
selectedCamera = 0
|
||||
selectedDate = None
|
||||
selectedTime = None
|
||||
imageList = []
|
||||
imageIndexWithinDate = None
|
||||
filename = None
|
||||
|
||||
def loading_screen():
|
||||
logo = 'lobstervision/lobsterpictures.gif'
|
||||
ugfx.area(0,0,ugfx.width(),ugfx.height(),0xFFFF)
|
||||
ugfx.display_image(0,50,logo)
|
||||
ugfx.set_default_font(ugfx.FONT_SMALL)
|
||||
ugfx.text(15, 305, "lobstervision.tv/emf2018", ugfx.GREY)
|
||||
display_loading()
|
||||
|
||||
def display_error(message):
|
||||
dialogs.notice(message, title='Error')
|
||||
|
||||
def display_loading():
|
||||
ugfx.area(0,215,320,25,0xFFFF)
|
||||
ugfx.set_default_font(ugfx.FONT_MEDIUM_BOLD)
|
||||
ugfx.text(90,220, "Loading...", ugfx.GREY)
|
||||
|
||||
def display_datetime():
|
||||
ugfx.area(0,215,320,25,0xFFFF)
|
||||
ugfx.set_default_font(ugfx.FONT_MEDIUM_BOLD)
|
||||
(date, day) = format_selected_date()
|
||||
time = format_selected_time()
|
||||
ugfx.text(5,220, time, ugfx.RED)
|
||||
ugfx.text(60,220, "%s, %s" % (day, date), ugfx.GREY)
|
||||
|
||||
def display_image():
|
||||
gc.collect()
|
||||
global selectedProject, selectedCamera, filename
|
||||
display_loading()
|
||||
endpoint = 'images/project/%d/camera/%d/%s' % \
|
||||
(selectedProject, selectedCamera, filename)
|
||||
try:
|
||||
headers = {'Authorization': 'Basic '+ACCESS_KEY}
|
||||
url = IMAGE_PROXY+endpoint
|
||||
get(url, headers = headers).raise_for_status().download_to(PHOTO_FILE)
|
||||
except OSError as e:
|
||||
display_error('Unable to download image %s' % e)
|
||||
return
|
||||
utime.sleep_ms(200)
|
||||
ugfx.display_image(0,0,PHOTO_FILE)
|
||||
display_datetime()
|
||||
|
||||
def format_selected_date():
|
||||
date = None
|
||||
day = None
|
||||
global selectedDate
|
||||
(year, month, day, hour, minute, second, dayofweek,
|
||||
dayinyear) = utime.localtime(selectedDate - 946684800)
|
||||
date = '%d %s %d' % (day, FULL_MONTHS[month-1], year)
|
||||
day = DAYS[dayofweek]
|
||||
return (date, day)
|
||||
|
||||
def format_selected_time():
|
||||
global selectedTime
|
||||
time = str(selectedTime)
|
||||
return '%s:%s' % (time[:2], time[2:4])
|
||||
|
||||
def list_cameras():
|
||||
global selectedProject
|
||||
cameraCount = len(projects[selectedProject]['camera'])
|
||||
cameras = []
|
||||
for i in range(0, cameraCount):
|
||||
cameras.append({'index': i, 'title': 'Camera %d' % (i + 1)})
|
||||
return cameras
|
||||
|
||||
def get_from_api(path):
|
||||
headers = {'Authorization': 'Basic '+ACCESS_KEY}
|
||||
url = IMAGE_PROXY+path
|
||||
with get(url, headers = headers) as response:
|
||||
return response.json()
|
||||
|
||||
def load_account_details():
|
||||
gc.collect()
|
||||
rsp = get_from_api('account')
|
||||
global projects
|
||||
if not 'result' in rsp:
|
||||
raise OSError('Could not load account data')
|
||||
if 'client' in rsp['result']:
|
||||
projects = rsp['result']['client']['project']
|
||||
|
||||
def load_camera_dates():
|
||||
gc.collect()
|
||||
for p, project in enumerate(projects):
|
||||
for c, camera in enumerate(project['camera']):
|
||||
endpoint = 'dates/project/%d/camera/%d' % (p, c)
|
||||
try:
|
||||
rsp = get_from_api(endpoint)
|
||||
except OSError:
|
||||
continue
|
||||
if not 'result' in rsp:
|
||||
continue
|
||||
camera['start'] = rsp['result']['start']
|
||||
camera['finish'] = rsp['result']['finish']
|
||||
camera['missing'] = rsp['result']['disabled']
|
||||
|
||||
def load_image_list():
|
||||
gc.collect()
|
||||
global selectedProject, selectedCamera, selectedDate, selectedTime, imageList
|
||||
if not selectedDate:
|
||||
# Bodge as EMF camera seems to have stalled uploading due to lack of
|
||||
# signal
|
||||
if projects[selectedProject]['camera'][selectedCamera]['finish'] == 1535673600:
|
||||
selectedDate = 1535587200
|
||||
selectedTime = "150000"
|
||||
else:
|
||||
selectedDate = projects[selectedProject]['camera']\
|
||||
[selectedCamera]['finish']
|
||||
endpoint = 'dates/project/%d/camera/%d/%s' % \
|
||||
(selectedProject, selectedCamera, selectedDate)
|
||||
try:
|
||||
rsp = get_from_api(endpoint)
|
||||
except OSError:
|
||||
return
|
||||
if not 'result' in rsp:
|
||||
return
|
||||
imageList = rsp['result']
|
||||
select_from_image_list()
|
||||
|
||||
def select_from_image_list():
|
||||
global imageList, selectedTime, imageIndexWithinDate, filename
|
||||
selectedImage = None
|
||||
firstImage = imageList[0]
|
||||
lastImage = imageList[-1]
|
||||
if not selectedTime or selectedTime >= lastImage['time']:
|
||||
selectedImage = lastImage
|
||||
imageIndexWithinDate = len(imageList) - 1
|
||||
elif selectedTime <= firstImage['time']:
|
||||
selectedImage = firstImage
|
||||
imageIndexWithinDate = 0
|
||||
else:
|
||||
previousDiff = 0
|
||||
for position, image in enumerate(imageList):
|
||||
diff = abs(int(image['time']) - int(selectedTime))
|
||||
if selectedTime < image['time']:
|
||||
if diff < previousDiff:
|
||||
selectedImage = image
|
||||
imageIndexWithinDate = position
|
||||
else:
|
||||
selectedImage = imageList[position - 1]
|
||||
imageIndexWithinDate= position - 1
|
||||
break
|
||||
previousDiff = diff
|
||||
if not selectedImage:
|
||||
selectedImage = lastImage
|
||||
imageIndexWithinDate = len(imageList) - 1
|
||||
selectedTime = selectedImage['time']
|
||||
filename = selectedImage['image']
|
||||
display_image()
|
||||
|
||||
def select_camera(camera):
|
||||
global selectedCamera, selectedDate, selectedTime
|
||||
selectedCamera = int(camera)
|
||||
selectedDate = None
|
||||
selectedTime = None
|
||||
load_image_list()
|
||||
|
||||
def select_date(date):
|
||||
global selectedDate
|
||||
selectedDate = int(date)
|
||||
load_image_list()
|
||||
|
||||
def previous_date():
|
||||
global selectedProject, selectedCamera, selectedDate
|
||||
camera = \
|
||||
projects[selectedProject]['camera'][selectedCamera]
|
||||
date = selectedDate - 86400 # 24 hours
|
||||
# Check not trying to go back before the camera's first day
|
||||
if date < camera['start']:
|
||||
return
|
||||
# Skip over any missing dates
|
||||
while date in camera['missing']:
|
||||
camera -= 86400
|
||||
print("Setting date to %s" % date)
|
||||
selectedDate = date
|
||||
load_image_list()
|
||||
|
||||
def next_date():
|
||||
global selectedProject, selectedCamera, selectedDate
|
||||
camera = \
|
||||
projects[selectedProject]['camera'][selectedCamera]
|
||||
date = selectedDate + 86400 # 24 hours
|
||||
# Check not trying to go back past the camera's last day
|
||||
if date > camera['finish']:
|
||||
return
|
||||
# Skip over any missing dates
|
||||
while date in camera['missing']:
|
||||
camera += 86400
|
||||
selectedDate = date
|
||||
load_image_list()
|
||||
|
||||
def previous_image():
|
||||
global selectedProject, selectedCamera, selectedDate, selectedTime
|
||||
global imageList, imageIndexWithinDate, filename
|
||||
# If first image of current day, jump to last image of previous day
|
||||
if imageIndexWithinDate == 0:
|
||||
camera = \
|
||||
projects[selectedProject]['camera'][selectedCamera]
|
||||
if selectedDate != camera['start']:
|
||||
selectedTime = None
|
||||
previous_date()
|
||||
return
|
||||
imageIndexWithinDate -= 1
|
||||
image = imageList[imageIndexWithinDate]
|
||||
filename = image['image']
|
||||
selectedTime = image['time']
|
||||
display_image()
|
||||
|
||||
def next_image():
|
||||
global selectedProject, selectedCamera, selectedDate, selectedTime
|
||||
global imageList, imageIndexWithinDate, filename
|
||||
# If first image of current day, jump to first image of next day
|
||||
if imageIndexWithinDate == len(imageList)-1:
|
||||
camera = \
|
||||
projects[selectedProject]['camera'][selectedCamera]
|
||||
if selectedDate != camera['finish']:
|
||||
selectedTime = '000000'
|
||||
next_date()
|
||||
return
|
||||
imageIndexWithinDate += 1
|
||||
image = imageList[imageIndexWithinDate]
|
||||
filename = image['image']
|
||||
selectedTime = image['time']
|
||||
display_image()
|
||||
|
||||
|
||||
def start():
|
||||
ugfx_helper.init()
|
||||
loading_screen()
|
||||
if not wifi.is_connected():
|
||||
try:
|
||||
wifi.connect()
|
||||
except OSError:
|
||||
display_error("Unable to connect to Wifi")
|
||||
return False
|
||||
try:
|
||||
load_account_details()
|
||||
except OSError as e:
|
||||
display_error("Unable to contact the server. Please try again later")
|
||||
return False
|
||||
if len(projects) == 0:
|
||||
display_error("Sorry, no projects are available to display")
|
||||
return False
|
||||
load_camera_dates()
|
||||
load_image_list()
|
||||
return True
|
||||
|
||||
running = start()
|
||||
while running:
|
||||
if buttons.is_triggered(Buttons.JOY_Right):
|
||||
next_image()
|
||||
elif buttons.is_triggered(buttons.Buttons.JOY_Left):
|
||||
previous_image()
|
||||
elif buttons.is_triggered(Buttons.JOY_Up):
|
||||
previous_date()
|
||||
elif buttons.is_triggered(Buttons.JOY_Down):
|
||||
next_date()
|
||||
utime.sleep_ms(30)
|
Loading…
Reference in New Issue