Merge remote-tracking branch 'emfcamp/master'

sammachin-gprs
EMF Villager 2018-09-01 21:50:52 +02:00
commit 84170bf6f3
25 changed files with 852 additions and 34 deletions

View File

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

View File

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

View File

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

View File

@ -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
.gitignore vendored
View File

@ -1,4 +1,3 @@
.DS_Store
__pycache__
wifi.json
wifi*.json

58
LED_Party/main.py Normal file

File diff suppressed because one or more lines are too long

63
beer/main.py Normal file
View File

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

60
dowsingrod/main.py Normal file
View File

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

106
game-of-life/main.py Normal file
View File

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

15
hello_world/main.py Normal file
View File

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

View File

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

View File

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

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)

View File

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

View File

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

61
review_helper/main.py Normal file
View File

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

View File

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

View File

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

BIN
stories/footer.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
stories/header.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

167
stories/main.py Executable file
View File

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

1
stories/stories.json Normal file
View File

@ -0,0 +1 @@
{ "has_warned" : false, "is_ok" : false }

1
wifi.json Normal file
View File

@ -0,0 +1 @@
{"ssid":"emfcamp-legacy18","user":"internetofstuff","pw":"internetofstuff"}