Merge remote-tracking branch 'emfcamp/master'
commit
84170bf6f3
|
@ -74,3 +74,8 @@ def clean(path=""):
|
|||
os.remove(full)
|
||||
except Exception as e:
|
||||
print("Error while trying to clean '%s'" % full)
|
||||
|
||||
try:
|
||||
os.remove("bootstrap.py")
|
||||
except:
|
||||
pass
|
||||
|
|
|
@ -81,7 +81,7 @@ def find_tty():
|
|||
|
||||
def check_run(paths):
|
||||
for filename in paths:
|
||||
with open(filename, 'r') as f:
|
||||
with open(filename, 'r', encoding='utf8') as f:
|
||||
pyfile = f.read()
|
||||
compile(pyfile + '\n', filename, 'exec')
|
||||
|
||||
|
@ -185,6 +185,8 @@ def write_via_repl(args, content, rel_path):
|
|||
h = hashlib.sha256()
|
||||
h.update(content)
|
||||
content = binascii.b2a_base64(content).decode('ascii').strip()
|
||||
if os.sep != '/':
|
||||
rel_path = rel_path.replace(os.sep, '/')
|
||||
rel_path_as_string = json.dumps(rel_path) # make sure quotes are escaped
|
||||
cmd = "h(%s)" % rel_path_as_string
|
||||
badge_hash = returnbuffer(pyb,cmd).splitlines()[0]
|
||||
|
|
|
@ -141,7 +141,7 @@ def add_metadata(path, resources):
|
|||
|
||||
if file:
|
||||
try:
|
||||
with open(os.path.join(path, file), "r") as stream:
|
||||
with open(os.path.join(path, file), "r", encoding='utf8') as stream:
|
||||
resource.update(_normalize_metadata(read_metadata(stream)))
|
||||
except ParseException as e:
|
||||
resource.setdefault("errors", []).append(file + ": " + str(e))
|
||||
|
@ -197,7 +197,7 @@ def _validate_resource(path, resource):
|
|||
if file.endswith(".py"):
|
||||
try:
|
||||
filename = os.path.join(path, file)
|
||||
with open(filename, 'r') as s:
|
||||
with open(filename, 'r', encoding='utf8') as s:
|
||||
compile(s.read() + '\n', filename, 'exec')
|
||||
except Exception as e:
|
||||
resource.setdefault("errors", []).append(str(e))
|
||||
|
|
|
@ -11,9 +11,6 @@ $ tilda_tools reset
|
|||
Soft reboot badge and start specific app
|
||||
$ tilda_tools reset --boot my_app
|
||||
|
||||
Update files on the badge to match the current local version, restarts afterwards
|
||||
$ tilda_tools sync
|
||||
|
||||
Update files in folder(s) to match current local version
|
||||
$ tilda_tools sync my_game shared
|
||||
$ tilda_tools sync <pattern1> <pattern2> ...
|
||||
|
@ -109,7 +106,8 @@ def main():
|
|||
if command == "test":
|
||||
command = "sync"
|
||||
if len(args.paths) == 0:
|
||||
args.paths = ["lib/test_*"]
|
||||
print("Please define an app or lib to sync: tilda_tools sync my_app\n")
|
||||
sys.exit(1)
|
||||
else:
|
||||
args.paths = ["lib/test_%s.py" % p for p in args.paths]
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
.DS_Store
|
||||
__pycache__
|
||||
wifi.json
|
||||
wifi*.json
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,63 @@
|
|||
"""What's on tap?!
|
||||
"""
|
||||
___name___ = "beer"
|
||||
___license___ = "MIT"
|
||||
___dependencies___ = ["app", "sleep", "wifi", "http", "ugfx_helper"]
|
||||
___categories___ = ["EMF"]
|
||||
|
||||
import wifi, ugfx, http, ujson, app, sleep
|
||||
from tilda import Buttons, LED
|
||||
|
||||
orientation = 270
|
||||
|
||||
def get_beer():
|
||||
global bar
|
||||
LED(LED.RED).on()
|
||||
try:
|
||||
bar_json = http.get("https://bar.emf.camp/location/Bar.json").raise_for_status().content
|
||||
bar = ujson.loads(bar_json)
|
||||
except:
|
||||
print('oh poop')
|
||||
LED(LED.RED).off()
|
||||
draw_screen()
|
||||
|
||||
def draw_screen():
|
||||
|
||||
ugfx.clear(ugfx.BLACK)
|
||||
ugfx.text(60, 5, "what's on tap?", ugfx.RED)
|
||||
ugfx.line(5, 20, ugfx.width(), 20, ugfx.GREY)
|
||||
for idx, beer in enumerate(bar['location']):
|
||||
ugfx.text(5, 22 + idx*15, beer['description'], ugfx.WHITE)
|
||||
|
||||
def toggle_orientation():
|
||||
global orientation
|
||||
if orientation == 90:
|
||||
ugfx.orientation(270)
|
||||
orientation = 270
|
||||
draw_screen()
|
||||
else:
|
||||
ugfx.orientation(90)
|
||||
orientation = 90
|
||||
draw_screen()
|
||||
|
||||
ugfx.init()
|
||||
ugfx.clear(ugfx.BLACK)
|
||||
|
||||
Buttons.enable_interrupt(Buttons.BTN_A, lambda button_id:get_beer(), on_press=True, on_release=False)
|
||||
Buttons.enable_interrupt(Buttons.BTN_B, lambda button_id:toggle_orientation(), on_press=True, on_release=False)
|
||||
Buttons.enable_interrupt(Buttons.BTN_Menu, lambda button_id:app.restart_to_default(), on_press=True, on_release=False)
|
||||
|
||||
|
||||
ugfx.text(5, 10, "Instructions:", ugfx.WHITE)
|
||||
ugfx.text(5, 30, "Press the A button to refresh", ugfx.WHITE)
|
||||
ugfx.text(5, 45, "Press the B button to rotate", ugfx.WHITE)
|
||||
ugfx.text(5, 60, "Press the Menu button to exit", ugfx.WHITE)
|
||||
ugfx.text(5, 95, "Loading data from the bar...", ugfx.WHITE)
|
||||
|
||||
get_beer()
|
||||
|
||||
while True:
|
||||
sleep.wfi()
|
||||
|
||||
ugfx.clear()
|
||||
app.restart_to_default()
|
|
@ -0,0 +1,60 @@
|
|||
"""This is a dowsing rod for WiFi APs"""
|
||||
|
||||
___name___ = "Dowsing Rod"
|
||||
___license___ = "MIT"
|
||||
___dependencies___ = ["sleep", "app", "wifi", "sim800"]
|
||||
___categories___ = ["EMF", "System"]
|
||||
|
||||
import ugfx, wifi, app
|
||||
from tilda import Buttons
|
||||
from time import sleep
|
||||
|
||||
status_height = 20
|
||||
ssid = 'emfcamp-legacy18'
|
||||
|
||||
ugfx.init()
|
||||
ugfx.clear()
|
||||
ugfx.set_default_font(ugfx.FONT_FIXED)
|
||||
|
||||
ugfx.Label(5, 180, 240, 15, "Press A to scan, MENU to exit")
|
||||
|
||||
# while (not Buttons.is_pressed(Buttons.BTN_A)) and (not Buttons.is_pressed(Buttons.BTN_B)) and (not Buttons.is_pressed(Buttons.BTN_Menu)):
|
||||
while not Buttons.is_pressed(Buttons.BTN_Menu):
|
||||
if not Buttons.is_pressed(Buttons.BTN_A) and not Buttons.is_pressed(Buttons.BTN_B):
|
||||
ugfx.poll()
|
||||
continue
|
||||
|
||||
if Buttons.is_pressed(Buttons.BTN_B):
|
||||
ugfx.clear()
|
||||
ugfx.Label(0, 0, 240, 25, "SSID:")
|
||||
ssid_box = ugfx.Textbox(0, 25, 240, 25, text=ssid)
|
||||
ugfx.Keyboard(0, ugfx.height()//2, ugfx.width(), ugfx.height()//2)
|
||||
ssid_box.set_focus()
|
||||
while not Buttons.is_pressed(Buttons.BTN_A):
|
||||
ugfx.poll()
|
||||
continue
|
||||
ssid = ssid_box.text()
|
||||
|
||||
ugfx.clear()
|
||||
|
||||
wifi.nic().active(False)
|
||||
wifi.nic().active(True)
|
||||
|
||||
# networks = [{ "ssid": ap[0], "mac": ap[1], "channel": ap[2], "signal": ap[3] } for ap in wifi.nic().scan()]
|
||||
networks = sorted([net for net in wifi.nic().scan() if net[0] == ssid], key=lambda n: n[3], reverse=True)
|
||||
|
||||
aps = []
|
||||
for ap in [(net[1], net[3]) for net in networks]:
|
||||
if ap[0] not in [ap[0] for ap in aps]:
|
||||
aps.append(ap)
|
||||
|
||||
y = 0
|
||||
for ap in aps[:20]:
|
||||
ugfx.Label(0, y, 240, 25, "{1}dB {0}".format(*ap))
|
||||
y += status_height
|
||||
|
||||
if len(aps) == 0:
|
||||
ugfx.Label(0, y, 240, 25, "No %s APs found" % ssid)
|
||||
|
||||
ugfx.clear()
|
||||
app.restart_to_default()
|
|
@ -0,0 +1,106 @@
|
|||
"""Game of Life"""
|
||||
|
||||
___name___ = "Conway game of life"
|
||||
___license___ = "MIT"
|
||||
___categories___ = ["Games"]
|
||||
___dependencies___ = ["app", "ugfx_helper", "random", "sleep", "buttons"]
|
||||
|
||||
import app, ugfx, ugfx_helper, buttons, sleep, time, random
|
||||
from tilda import Buttons
|
||||
|
||||
|
||||
# the game of life logic
|
||||
class Board:
|
||||
def __init__(self, width, height):
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.data = [random.randint(0,1) for x in range(width * height)]
|
||||
|
||||
def __str__(self):
|
||||
res = "w: {} h: {}".format(self.width, self.height)
|
||||
for j in range(0, self.height):
|
||||
row = [self.value(i, j) for i in range(self.width)]
|
||||
res = res + "\n" + row
|
||||
return res
|
||||
|
||||
def value(self, x, y):
|
||||
return self.data[x * self.width + y]
|
||||
|
||||
def neighbours(self, x, y):
|
||||
neighbCoords = [(i, j)
|
||||
for i in range(x - 1, x + 2) if i >= 0 and i < self.width
|
||||
for j in range(y - 1, y + 2) if j >= 0 and j < self.height
|
||||
]
|
||||
|
||||
return [self.value(neighbCoord[0], neighbCoord[1])
|
||||
for neighbCoord in neighbCoords if neighbCoord != (x, y) ]
|
||||
|
||||
# returns the new value of a given cell
|
||||
def nextValue(self, x, y):
|
||||
neighbsArr = self.neighbours(x, y)
|
||||
liveNeighbs = 0
|
||||
for neighb in neighbsArr:
|
||||
if (neighb):
|
||||
liveNeighbs = liveNeighbs + 1
|
||||
|
||||
if(self.value(x, y)):
|
||||
if (liveNeighbs <= 1):
|
||||
return 0 # underpopulation
|
||||
else:
|
||||
if (liveNeighbs <= 3):
|
||||
return 1 # lives
|
||||
else:
|
||||
return 0 # overpopulation
|
||||
else:
|
||||
if (liveNeighbs == 3):
|
||||
return 1 # reproduction
|
||||
else:
|
||||
return 0 # dies
|
||||
|
||||
# update the board data in place
|
||||
def step(self):
|
||||
self.data = [self.nextValue(x, y) for x in range(self.width) for y in range(self.height)]
|
||||
|
||||
|
||||
|
||||
# now the displaying part
|
||||
|
||||
ugfx_helper.init()
|
||||
ugfx.clear()
|
||||
|
||||
|
||||
grid_size = 5
|
||||
grid_width = round(ugfx.width() / grid_size)
|
||||
grid_height = round(ugfx.height() / grid_size)
|
||||
alive_colours = [ugfx.WHITE, ugfx.GRAY, ugfx.BLUE, ugfx.RED, ugfx.GREEN, ugfx.YELLOW, ugfx.ORANGE]
|
||||
dead_colour = ugfx.BLACK
|
||||
|
||||
def displayCell(x, y, alive):
|
||||
if(alive):
|
||||
colour = alive_colours[random.randrange(len(alive_colours))]
|
||||
else:
|
||||
colour = dead_colour
|
||||
ugfx.area(x*grid_size, y*grid_size, grid_size, grid_size, colour)
|
||||
|
||||
|
||||
def displayBoard(board):
|
||||
coords = [(x, y) for x in range(board.width) for y in range(board.height)]
|
||||
for (x, y) in coords:
|
||||
displayCell(x, y, board.value(x, y))
|
||||
|
||||
|
||||
|
||||
|
||||
board = Board(grid_width, grid_height)
|
||||
while True:
|
||||
displayBoard(board)
|
||||
board.step()
|
||||
#time.sleep(1)
|
||||
|
||||
sleep.wfi()
|
||||
if buttons.is_triggered(Buttons.BTN_Menu):
|
||||
break
|
||||
|
||||
|
||||
ugfx.clear()
|
||||
app.restart_to_default()
|
|
@ -0,0 +1,15 @@
|
|||
"""This is a simple hello world app"""
|
||||
|
||||
___name___ = "Hello World"
|
||||
___license___ = "MIT"
|
||||
___dependencies___ = ["sleep", "app"]
|
||||
___categories___ = ["EMF"]
|
||||
|
||||
import ugfx, os, time, sleep, app
|
||||
|
||||
|
||||
# initialize screen
|
||||
ugfx.init()
|
||||
ugfx.clear()
|
||||
|
||||
ugfx.text(5, 5, "Hello World!", ugfx.BLACK)
|
|
@ -15,13 +15,16 @@ ___bootstrapped___ = True
|
|||
import ugfx
|
||||
from homescreen import *
|
||||
import time
|
||||
from tilda import Buttons
|
||||
|
||||
# We ❤️ our sponsors
|
||||
init()
|
||||
ugfx.display_image(0, 0, "shared/sponsors.png")
|
||||
wait = 5
|
||||
while wait:
|
||||
wait-=1
|
||||
sleep_or_exit(0.5)
|
||||
wait_until = time.ticks_ms() + 3000
|
||||
while time.ticks_ms() < wait_until:
|
||||
time.sleep(0.1)
|
||||
if Buttons.is_pressed(Buttons.BTN_A) or Buttons.is_pressed(Buttons.BTN_B) or Buttons.is_pressed(Buttons.BTN_Menu):
|
||||
break
|
||||
|
||||
# Padding for name
|
||||
intro_height = 30
|
||||
|
@ -37,7 +40,7 @@ logo_width = 56
|
|||
max_name = 8
|
||||
|
||||
# Background stuff
|
||||
init()
|
||||
|
||||
ugfx.clear(ugfx.html_color(0x800080))
|
||||
|
||||
# Colour stuff
|
||||
|
@ -77,7 +80,7 @@ ugfx.orientation(270)
|
|||
ugfx.set_default_font(ugfx.FONT_TITLE)
|
||||
ugfx.Label(0, ugfx.height() - info_height * 2, ugfx.width(), info_height, "TiLDA Mk4", justification=ugfx.Label.CENTER)
|
||||
# info
|
||||
ugfx.Label(0, ugfx.height() - info_height, ugfx.width(), info_height, "Press MENU", justification=ugfx.Label.CENTER)
|
||||
ugfx.Label(0, ugfx.height() - info_height, ugfx.width(), info_height, "Long Press MENU", justification=ugfx.Label.CENTER)
|
||||
|
||||
ugfx.set_default_font(ugfx.FONT_SMALL)
|
||||
status = ugfx.Label(0, ugfx.height() - info_height * 2 - status_height, ugfx.width(), status_height, "", justification=ugfx.Label.CENTER)
|
||||
|
|
|
@ -28,6 +28,9 @@ class BadgeStore:
|
|||
def get_app(self, app):
|
||||
return self._call("app", {"app": app})
|
||||
|
||||
def get_prs(self):
|
||||
return self._call("prs")
|
||||
|
||||
def install(self, apps):
|
||||
return self._create_installers(self._call("install", {"apps": ",".join(apps)}))
|
||||
|
||||
|
|
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)
|
|
@ -6,13 +6,14 @@ ___dependencies___ = ["dialogs", "ugfx_helper"]
|
|||
___categories___ = ["EMF"]
|
||||
___bootstrapped___ = True
|
||||
|
||||
import ugfx, tilda, ugfx_helper, dialogs, app
|
||||
import ugfx, tilda, ugfx_helper, dialogs, app, time
|
||||
|
||||
ugfx_helper.init()
|
||||
ugfx.clear()
|
||||
|
||||
print("enabling USB storage...")
|
||||
tilda.storage_enable_usb()
|
||||
time.sleep(1)
|
||||
print("DONE")
|
||||
with dialogs.WaitingMessage(title="Mass Storage Enabled", text="You can now use the badge like a USB key.\nPlease safely eject afterwards. This app will close automatically."):
|
||||
print("Waiting for USB mass storage to be unmounted...")
|
||||
|
|
|
@ -29,7 +29,7 @@ def answercall():
|
|||
else:
|
||||
notice("No call to answer.", title="TiLDA Phone")
|
||||
|
||||
def handupcall():
|
||||
def hangupcall():
|
||||
sim800.hangup()
|
||||
|
||||
def playdtmf():
|
||||
|
@ -38,37 +38,26 @@ def playdtmf():
|
|||
while True:
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_0):
|
||||
sim800.dtmf("0")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_1):
|
||||
sim800.dtmf("1")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_2):
|
||||
sim800.dtmf("2")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_3):
|
||||
sim800.dtmf("3")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_4):
|
||||
sim800.dtmf("4")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_5):
|
||||
sim800.dtmf("5")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_6):
|
||||
sim800.dtmf("6")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_7):
|
||||
sim800.dtmf("7")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_8):
|
||||
sim800.dtmf("8")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_9):
|
||||
sim800.dtmf("9")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_Star):
|
||||
sim800.dtmf("*")
|
||||
time.sleep(1)
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_Hash):
|
||||
sim800.dtmf("#")
|
||||
if buttons.is_pressed(buttons.Buttons.BTN_A):
|
||||
|
@ -142,7 +131,7 @@ def selectoperator():
|
|||
if selectedop:
|
||||
if selectedop["index"]==-1:
|
||||
sim800.setoperator(0)
|
||||
notice("operator selection set to automatic.", title="TiLDA Phone")
|
||||
notice("Operator selection set to automatic.", title="TiLDA Phone")
|
||||
else:
|
||||
sim800.setoperator(1,2,selectedop["index"])
|
||||
notice(selectedop["title"] + " set as operator.", title="TiLDA Phone")
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
"""Helps to test incoming PRs"""
|
||||
|
||||
___name___ = "PR Review Helper"
|
||||
___license___ = "MIT"
|
||||
___categories___ = ["System"]
|
||||
___dependencies___ = ["dialogs", "app", "ugfx_helper", "badge_store", "http", "stack_nav", "wifi"]
|
||||
|
||||
import ugfx_helper, ugfx, wifi
|
||||
from app import *
|
||||
from dialogs import *
|
||||
from stack_nav import *
|
||||
from lib.badge_store import BadgeStore
|
||||
|
||||
title = "PR Review Helper"
|
||||
|
||||
def install(state):
|
||||
apps = set()
|
||||
with WaitingMessage(title="Fetching apps", text="Please wait...") as message:
|
||||
for category, a in state["store"].get_all_apps().items():
|
||||
apps.update(a)
|
||||
|
||||
menu_items = [{"title": a, "app": a} for a in apps]
|
||||
|
||||
option = prompt_option(menu_items, none_text="Back", title="title")
|
||||
|
||||
if option:
|
||||
state["app"] = option
|
||||
return show_app
|
||||
|
||||
def show_app(state):
|
||||
a = state["app"]["app"]
|
||||
with WaitingMessage(title="Installing %s" % a, text="Please wait...") as message:
|
||||
apps_to_install = []#[a.name for a in get_apps()]
|
||||
apps_to_install.append(a)
|
||||
print(apps_to_install)
|
||||
installers = state["store"].install(apps_to_install)
|
||||
n = len(installers)
|
||||
for i, installer in enumerate(installers):
|
||||
message.text = "%s (%s/%s)" % (installer.path, i + 1, n)
|
||||
installer.download()
|
||||
|
||||
notice("App %s has been successfully installed" % a, title=title, close_text="Run it!")
|
||||
App(a).boot()
|
||||
|
||||
def entry_point(state):
|
||||
url = database.get("badge_store.url", "http://badgeserver.emfcamp.org/2018")
|
||||
repo = database.get("badge_store.repo", "emfcamp/Mk4-Apps")
|
||||
store = BadgeStore(url=url, repo=repo)
|
||||
|
||||
prs = store.get_prs()
|
||||
selection = prompt_option(prs, text="Select PR", none_text="Exit", title=title)
|
||||
if selection:
|
||||
state["store"] = BadgeStore(url=url, repo=repo, ref=selection["ref"])
|
||||
return install
|
||||
|
||||
|
||||
### ENTRY POINT ###
|
||||
ugfx_helper.init()
|
||||
wifi.connect(show_wait_message=True)
|
||||
nav(entry_point)
|
||||
app.restart_to_default()
|
|
@ -24,8 +24,8 @@ def send_message():
|
|||
number = ""
|
||||
message = ""
|
||||
while True:
|
||||
num = prompt_text("Number to message:", init_text=number)
|
||||
if num is None:
|
||||
number = prompt_text("Number to message:", init_text=number)
|
||||
if number is None:
|
||||
return
|
||||
message = prompt_text("Message:", init_text=message)
|
||||
if message is not None:
|
||||
|
|
|
@ -62,19 +62,19 @@ def one_round():
|
|||
# disp_body_straight(body_x[i],body_y[i],orient,body_colour)
|
||||
|
||||
while keepgoing:
|
||||
if Buttons.is_pressed(Buttons.JOY_Right) or Buttons.is_pressed(Buttons.BTN_6):
|
||||
if dir_x != -1 and (Buttons.is_pressed(Buttons.JOY_Right) or Buttons.is_pressed(Buttons.BTN_6)):
|
||||
dir_x = 1;
|
||||
dir_y = 0;
|
||||
orient = 270
|
||||
elif Buttons.is_pressed(Buttons.JOY_Left) or Buttons.is_pressed(Buttons.BTN_4):
|
||||
elif dir_x != 1 and (Buttons.is_pressed(Buttons.JOY_Left) or Buttons.is_pressed(Buttons.BTN_4)):
|
||||
dir_x = -1;
|
||||
dir_y = 0;
|
||||
orient = 90
|
||||
elif Buttons.is_pressed(Buttons.JOY_Down) or Buttons.is_pressed(Buttons.BTN_8):
|
||||
elif dir_y != -1 and (Buttons.is_pressed(Buttons.JOY_Down) or Buttons.is_pressed(Buttons.BTN_8)):
|
||||
dir_y = 1;
|
||||
dir_x = 0;
|
||||
orient = 180
|
||||
elif Buttons.is_pressed(Buttons.JOY_Up) or Buttons.is_pressed(Buttons.BTN_0):
|
||||
elif dir_y != 1 and (Buttons.is_pressed(Buttons.JOY_Up) or Buttons.is_pressed(Buttons.BTN_0)):
|
||||
dir_y = -1;
|
||||
dir_x = 0;
|
||||
orient = 0
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,167 @@
|
|||
"""Read stories from twentythreemillionstories.org"""
|
||||
|
||||
___name___ = "twenty-three million stories"
|
||||
___license___ = "MIT"
|
||||
___categories___ = ["Other"]
|
||||
___dependencies___ = [ "app", "dialogs", "http", "ugfx_helper", "sleep" ]
|
||||
___bootstrapped___ = False
|
||||
|
||||
|
||||
from app import *
|
||||
from dialogs import *
|
||||
|
||||
import ugfx_helper, ugfx, http, sleep, os, json
|
||||
from tilda import Buttons
|
||||
|
||||
|
||||
_STORY_URL = "http://twentythreemillionstories.org/api/story/generate"
|
||||
_LOADING_TEXT = "Fetching a story..."
|
||||
_FAIL_TEXT = "An artist wants to write all the stories in the world. She meets a cartographer and an inventor. The inventor makes a machine to make old stories new. The new stories are stranger than the originals, but the machine needs an internet connection. This is a great pity and the reader is sad."
|
||||
|
||||
_LABEL = None
|
||||
|
||||
|
||||
def fetch_story():
|
||||
|
||||
# use for testing when wifi fails
|
||||
# return "A man falls in love with two twin sisters and is unable to decide which one to marry. Only the children survive. Through the window, the boy can see two big cranes in a construction site. But he loves her so much by now that he goes on with the marriage and then, he just waits."
|
||||
|
||||
# print( "fetch_story" )
|
||||
story = None
|
||||
|
||||
with http.get( _STORY_URL ) as response:
|
||||
if response.status != 200:
|
||||
return _FAIL_TEXT
|
||||
story = response.raise_for_status().json()
|
||||
|
||||
# {
|
||||
# number: "61,957",
|
||||
# story: "A man falls in love with two twin sisters and is unable to decide which one to marry. Only the children survive. Through the window, the boy can see two big cranes in a construction site. But he loves her so much by now that he goes on with the marriage and then, he just waits."
|
||||
# }
|
||||
|
||||
return story["story"]
|
||||
|
||||
|
||||
def draw_furniture():
|
||||
ugfx.display_image( 0, 0, "stories/header.gif" )
|
||||
ugfx.display_image( 0, 320-29, "stories/footer.gif" )
|
||||
|
||||
|
||||
def display_story( story ):
|
||||
global _LABEL
|
||||
#_LABEL = init_label( win )
|
||||
# print( "display_story: %s" % story )
|
||||
ugfx.clear( ugfx.WHITE )
|
||||
draw_furniture()
|
||||
_LABEL.text( story )
|
||||
|
||||
|
||||
|
||||
def fetch_and_display():
|
||||
display_story( _LOADING_TEXT )
|
||||
story = fetch_story()
|
||||
display_story( story )
|
||||
|
||||
|
||||
def init_label():
|
||||
|
||||
ugfx.set_default_font( ugfx.FONT_MEDIUM )
|
||||
|
||||
margin_v = 29
|
||||
margin_h = 18
|
||||
|
||||
screen_w = 240
|
||||
screen_h = 320
|
||||
|
||||
return ugfx.Label(
|
||||
margin_h, margin_v, #x, y
|
||||
screen_w - (margin_h*2) , screen_h - (margin_v*2), # width, height
|
||||
_LOADING_TEXT
|
||||
)
|
||||
|
||||
|
||||
def init():
|
||||
# Background stuff
|
||||
ugfx_helper.init()
|
||||
ugfx.clear( ugfx.WHITE )
|
||||
|
||||
# Colour stuff
|
||||
color = ugfx.html_color(333333)
|
||||
style = ugfx.Style()
|
||||
# [text_colour, edge_colour, fill_colour, progress_colour]
|
||||
style.set_enabled([color,color,ugfx.WHITE,ugfx.GREY])
|
||||
style.set_background( ugfx.WHITE )
|
||||
ugfx.set_default_style( style )
|
||||
|
||||
|
||||
_CONFIG_FILE = "stories/stories.json"
|
||||
|
||||
|
||||
def write_config( config ):
|
||||
with open( _CONFIG_FILE, "wt") as file:
|
||||
file.write( json.dumps(config) )
|
||||
file.flush()
|
||||
os.sync()
|
||||
|
||||
|
||||
def read_config():
|
||||
config = None
|
||||
with open( _CONFIG_FILE, "rt") as file:
|
||||
r = file.read()
|
||||
print( r )
|
||||
config = json.loads( r )
|
||||
return config
|
||||
|
||||
|
||||
def check_warning():
|
||||
config = read_config()
|
||||
|
||||
if config["has_warned"]:
|
||||
return config["is_ok"]
|
||||
|
||||
content_ok = prompt_boolean(
|
||||
"""Some (not many) of the Twenty-three million stories contain potentially triggering content.\n
|
||||
If that's OK by you, press A to start reading.""",
|
||||
title="Content Warning"
|
||||
)
|
||||
|
||||
config = {
|
||||
"has_warned" : True,
|
||||
"is_ok" : content_ok
|
||||
}
|
||||
|
||||
write_config( config )
|
||||
|
||||
return content_ok
|
||||
|
||||
|
||||
##
|
||||
# MAIN RUNLOOP
|
||||
#
|
||||
|
||||
init()
|
||||
|
||||
if check_warning():
|
||||
|
||||
|
||||
_LABEL = init_label()
|
||||
fetch_and_display()
|
||||
|
||||
while True:
|
||||
|
||||
sleep.wfi()
|
||||
|
||||
if Buttons.is_pressed( Buttons.BTN_A ):
|
||||
fetch_and_display()
|
||||
|
||||
elif Buttons.is_pressed( Buttons.BTN_Menu ) or \
|
||||
Buttons.is_pressed( Buttons.BTN_B ) or \
|
||||
Buttons.is_pressed( Buttons.JOY_Center):
|
||||
break
|
||||
|
||||
# print ("Stories ded...")
|
||||
if _LABEL:
|
||||
_LABEL.destroy()
|
||||
|
||||
ugfx.clear()
|
||||
|
|
@ -0,0 +1 @@
|
|||
{ "has_warned" : false, "is_ok" : false }
|
Loading…
Reference in New Issue