From d63360c4aecd5bb8a01acd08a2b2d79b6896bf3d Mon Sep 17 00:00:00 2001 From: Pete Redhead Date: Sat, 1 Sep 2018 14:41:57 +0100 Subject: [PATCH] Adds Lobster Vision app (viewer for timelapse camera recording site build) --- lobstervision/.main.py.swp | Bin 0 -> 28672 bytes lobstervision/lobsterpictures.gif | Bin 0 -> 6012 bytes lobstervision/main.py | 286 ++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 lobstervision/.main.py.swp create mode 100644 lobstervision/lobsterpictures.gif create mode 100644 lobstervision/main.py diff --git a/lobstervision/.main.py.swp b/lobstervision/.main.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..27793fc5859def356d01721edf031198949d7fd4 GIT binary patch literal 28672 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 literal 0 HcmV?d00001 diff --git a/lobstervision/main.py b/lobstervision/main.py new file mode 100644 index 0000000..947132c --- /dev/null +++ b/lobstervision/main.py @@ -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)