Adds Lobster Vision app (viewer for timelapse camera recording site build)

sammachin-gprs
Pete Redhead 2018-09-01 14:41:57 +01:00
parent 0c40c9ee1e
commit d63360c4ae
3 changed files with 286 additions and 0 deletions

BIN
lobstervision/.main.py.swp Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

286
lobstervision/main.py Normal file
View File

@ -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)