diff --git a/.development/tilda_tools.py b/.development/tilda_tools.py index 15f1fa9..44424f3 100755 --- a/.development/tilda_tools.py +++ b/.development/tilda_tools.py @@ -61,6 +61,7 @@ def main(): cmd_parser.add_argument('-b', '--baudrate', default=115200, help='the baud rate of the serial device') cmd_parser.add_argument('-v', '--verbose', action='store_true', help='adds more output') cmd_parser.add_argument('--skip-wifi', action='store_true', help='does not sync wifi.json') + cmd_parser.add_argument('--bootstrapped-apps', action='store_true', help='[Sync] only bootstrapped apps by default') cmd_parser.add_argument('--print_resources', action='store_true', help='prints resources in json') cmd_parser.add_argument('--boot', help='defines which app to boot into after reboot') cmd_parser.add_argument('--run', help='like run, but after a sync') @@ -120,9 +121,17 @@ def main(): pyboard_util.hard_reset(args) if command == "sync": + paths = args.paths if len(args.paths) else None + if args.bootstrapped_apps: + for k,val in list(resources.items()): + if val.get("type", None) == "app": + if not k in paths and not val.get("bootstrapped", False): + if args.verbose: + print("Removing app '{0}' from sync list".format(k)) + del resources[k] + if args.clean: sync.clean(args) - paths = args.paths if len(args.paths) else None synced_resources = sync.sync(args, paths, resources, args.verbose, args.skip_wifi) if (command in ["reset", "sync"]) or run_tests: diff --git a/3dspin/main.py b/3dspin/main.py index 4cff9f8..c444b4a 100644 --- a/3dspin/main.py +++ b/3dspin/main.py @@ -1,9 +1,9 @@ """3d rotating polyhedra. 2016 badge competition winner, ported for 2018!""" -___name___ = "3D Spin" +___title___ = "3D Spin" ___license___ = "MIT" ___categories___ = ["Demo"] -___dependencies___ = ["app", "ugfx_helper", "random", "sleep", "buttons"] +___dependencies___ = ["app", "ugfx_helper", "sleep", "buttons"] import ugfx from tilda import Buttons diff --git a/LED_Party/main.py b/LED_Party/main.py index a6c508d..e144681 100644 --- a/LED_Party/main.py +++ b/LED_Party/main.py @@ -1,6 +1,6 @@ """ starts an LED party on your badge """ -___name___ = "LED Party (Party Party)" +___title___ = "LED Party (Party Party)" ___license___ = "MIT" ___dependencies___ = ["wifi", "http", "ugfx_helper", "sleep"] ___categories___ = ["LEDs"] diff --git a/SketchyEtch/main.py b/SketchyEtch/main.py index f9d3ef2..af66250 100644 --- a/SketchyEtch/main.py +++ b/SketchyEtch/main.py @@ -1,25 +1,40 @@ """Accidentally created etcher sketch...""" -___name___ = "Sketchy Etch" -___title___ = "Sketchy Etch" +___name___ = "Sketchy-Etch" +___title___ = "Sketchy-Etch" ___license___ = "MIT" -___dependencies___ = ["ugfx_helper"] +___dependencies___ = ["ugfx_helper", "dialogs"] ___categories___ = ["Games"] -import ugfx, ugfx_helper, app +import ugfx, ugfx_helper, app, dialogs from tilda import Buttons from time import sleep +i = 0 +j = 0 + +def reset(): + global i + global j + i = int(ugfx.width() / 2) + j = int(ugfx.height() / 2) + ugfx.area(0, 0, ugfx.width(), ugfx.height(), ugfx.BLACK) + ugfx.area((i - 1) if i > 0 else 0, (j - 1) if j > 0 else 0, 3 if (i > 0 and i < (ugfx.width() - 1)) else 2, 3 if (j > 0 and j < (ugfx.height() - 1)) else 2, ugfx.GREY) + ugfx_helper.init() ugfx.clear() -ugfx.area(0, 0, ugfx.width(), ugfx.height(), ugfx.BLACK) - +dialogs.notice("Draw with joystick arrows\nHold joystick centre for circle\nA to clear\nB to exit", title="Sketchy-Etch") -i = int(ugfx.width() / 2) -j = int(ugfx.height() / 2) -while (not Buttons.is_pressed(Buttons.BTN_A)) and (not Buttons.is_pressed(Buttons.BTN_B)) and (not Buttons.is_pressed(Buttons.BTN_Menu)): +ugfx.area(0, 0, ugfx.width(), ugfx.height(), ugfx.BLACK) + + +circleSize = 3 +reset() +while not Buttons.is_pressed(Buttons.BTN_B): changed = False + oldI = i + oldJ = j if Buttons.is_pressed(Buttons.JOY_Right) and (i < (ugfx.width() - 1)): i += 1 @@ -34,9 +49,20 @@ while (not Buttons.is_pressed(Buttons.BTN_A)) and (not Buttons.is_pressed(Button elif Buttons.is_pressed(Buttons.JOY_Up) and (j > 0): j -= 1 changed = True + + if Buttons.is_pressed(Buttons.JOY_Center): + circleSize += 1 + ugfx.fill_circle(i, j, circleSize, ugfx.WHITE) + changed = True + else: + circleSize = 3 + + if Buttons.is_pressed(Buttons.BTN_A): + reset() if changed: - ugfx.area((i - 1) if i > 0 else 0, (j - 1) if j > 0 else 0, 3 if (i > 0 and i < (ugfx.width() - 1)) else 2, 3 if (j > 0 and j < (ugfx.height() - 1)) else 2, ugfx.WHITE) + ugfx.area((oldI - 1) if oldI > 0 else 0, (oldJ - 1) if oldJ > 0 else 0, 3 if (oldI > 0 and oldI < (ugfx.width() - 1)) else 2, 3 if (oldJ > 0 and oldJ < (ugfx.height() - 1)) else 2, ugfx.WHITE) + ugfx.area((i - 1) if i > 0 else 0, (j - 1) if j > 0 else 0, 3 if (i > 0 and i < (ugfx.width() - 1)) else 2, 3 if (j > 0 and j < (ugfx.height() - 1)) else 2, ugfx.GREY) sleep(0.05) diff --git a/avatar/main.py b/avatar/main.py index e88fe6a..ddf021f 100644 --- a/avatar/main.py +++ b/avatar/main.py @@ -1,6 +1,6 @@ """A simple homescreen diplaying an avatar from an url and the user's name""" -___name___ = "Avatar Homescreen" +___title___ = "Avatar Homescreen" ___license___ = "WTFPL" ___categories___ = ["Homescreens"] ___dependencies___ = ["homescreen", "wifi", "http", "sleep", "app", "buttons"] diff --git a/badge_store/main.py b/badge_store/main.py index 48e85cb..fbf8d29 100644 --- a/badge_store/main.py +++ b/badge_store/main.py @@ -66,7 +66,7 @@ def show_app(a,c): if install: app_text = "App:\n{}\n\n".format(name) with WaitingMessage(title="Installing App...", text="%sGetting ready..." % app_text) as message: - installers = store.install(_get_current_apps() + [a]) + installers = store.install([a]) n = len(installers) for i, installer in enumerate(installers): message.text = "%s%s (%s/%s)" % (app_text + "Downloading files...\n\n", installer.path, i + 1, n) diff --git a/badgesimulator/main.py b/badgesimulator/main.py index ccfc8a4..33b0bd7 100644 --- a/badgesimulator/main.py +++ b/badgesimulator/main.py @@ -1,6 +1,6 @@ """This app creates a real EMF badge experience""" -___name___ = "EMF 2018 badge simulator" +___title___ = "EMF 2018 badge simulator" ___license___ = "MIT" ___categories___ = ["EMF"] ___dependencies___ = ["sleep", "app"] diff --git a/basic_clock/main.py b/basic_clock/main.py index 7d2b8b4..3ed86b6 100644 --- a/basic_clock/main.py +++ b/basic_clock/main.py @@ -1,5 +1,6 @@ """An NTP time app""" -___name___ = "NTP time" + +___title___ = "NTP time" ___license___ = "MIT" ___dependencies___ = ["ntp", "wifi", "app"] ___categories___ = ["EMF"] diff --git a/beer/main.py b/beer/main.py index 92c3d4f..78c894d 100644 --- a/beer/main.py +++ b/beer/main.py @@ -2,7 +2,7 @@ Get up to date information on what's in stock at The Robot Arms! """ -___name___ = "beer" +___title___ = "beer" ___license___ = "MIT" ___dependencies___ = ["app", "sleep", "wifi", "http", "ugfx_helper"] ___categories___ = ["EMF"] diff --git a/bluetooth_speaker/main.py b/bluetooth_speaker/main.py new file mode 100644 index 0000000..32c8c17 --- /dev/null +++ b/bluetooth_speaker/main.py @@ -0,0 +1,141 @@ +"""App to use the badge as a (handset profile only) bluetooth speaker""" + +___name___ = "Bluetooth Speaker" +___license___ = "MIT" +___dependencies___ = ["ugfx_helper", "sim800", "dialogs", "buttons", "app"] +___categories___ = ["Sound"] + + +import ugfx_helper, ugfx +import app +import sim800 +from dialogs import * +import buttons + +BLUETOOTH_NAME = "BadgeSpeaker" + +g_paired = False + + +def pairing_dialog(scan_timeout_s=10): + ''' Show BLE devices to pair with and connect. Returns True if paired, False if failed ''' + waiting_message = WaitingMessage("Scanning for bluetooth devices for %s seconds"%scan_timeout_s, "Scanning") + + + devices = sim800.btscan(int(scan_timeout_s * 1000)) + + waiting_message.destroy() + + # List format is [id, name, addr, rssi]. FIXME: Only returns 1 item? + try: + devices_prompts = [{'title': v[1], 'id': v[0]} for v in devices] + except TypeError: #Only one device found. #TODO: Not very neat + devices_prompts = [{'title':devices[1] ,'id':devices[0]},] + + #TODO: Fix non printable chars in device names + + option = prompt_option(devices_prompts, title="Devices Found", select_text="Select", none_text="Rescan") + + if option: + sim800.btpair(option['id']) + passcode = sim800.btparingpasscode() + correct_passcode = prompt_boolean(passcode, title="Started connection from other device?", font=FONT_MEDIUM_BOLD) + + if correct_passcode: + sim800.btpairconfirm() #TODO: 4 number passcodes? + return True + + else: + sim800.btpairreject() + return False + else: + return False + + +def pairing_callback(param): + ''' Callback for incoming pairing request ''' + global g_paired + accept = prompt_boolean("Accept pairing request from %s"%param, title="Incoming pairing") + if accept: + sim800.btpairconfirm(0000) + # Check if we did pair + if len(sim800.btpaired()) > 1: + g_paired = True + else: + sim800.btpairreject() + + +def set_simple_pairing(): + ''' Set pairing mode to 4 digit pin, default 0000 ''' + sim800.command("AT+BTPAIRCFG=1,0000", 1000, "OK") # TODO: Error checking? + + +#Initialise +ugfx_helper.init() +ugfx.init() +ugfx.clear() + +ugfx.text(5,5, "Powering Up SIM800", ugfx.BLACK) +sim800.poweron() +ugfx.clear() + +ugfx.text(5,5, "Enabling Bluetooth", ugfx.BLACK) +sim800.btpoweron() +sim800.btname(BLUETOOTH_NAME) +sim800.poweroff() +sim800.poweron() +sim800.btpoweron() # Needs a full cycle to have an effect +sim800.btvisible(True) + +# Set pairing mode +set_simple_pairing() + +ugfx.text(5,20, "Addr: %s " % sim800.btaddress(), ugfx.BLACK) +ugfx.text(5,35, "Name: %s " % sim800.btname(), ugfx.BLACK) +ugfx.clear() + +# Register pairings callback +sim800.registercallback("+BTPAIRING:", pairing_callback) + +clear_pairing = prompt_boolean("Delete all bluetooth pairings?",title="Clear Pairings?", true_text="Yes", false_text="No") + +if clear_pairing: + sim800.btunpair(0) #0 = clear every pairing + +# Start main loop +ugfx.clear() +ugfx.Label(5,5, 220, 200, "Connect to %s \n Passcode = 0000 \n Press menu to exit" % BLUETOOTH_NAME) + +connected = True + +while(True): + + # Check for pairing button + if (buttons.is_triggered(buttons.Buttons.BTN_1)): + pairing_dialog() + + # Check for exit button + if (buttons.is_triggered(buttons.Buttons.BTN_Menu)): + sim800.btpoweroff() + app.restart_to_default() + + num_connections = len(sim800.btconnected()) + + if (connected == False) and (num_connections > 0): # Gained connection + ugfx.area(0,220,240,320, ugfx.BLACK) #Blank bottom of screen + print(sim800.btconnected()) + sim800.speakervolume(100) + sim800.btvoicevolume(100) + ugfx.set_default_font(ugfx.FONT_TITLE) + ugfx.text(5,230,"CONNECTED!", ugfx.GREEN) + ugfx.set_default_font(ugfx.FONT_SMALL) + connected = True + + elif (connected == True) and (num_connections == 0): # Lost connection + ugfx.area(0,220,240,320, ugfx.BLACK) #Blank bottom of screen + ugfx.set_default_font(ugfx.FONT_TITLE) + ugfx.text(5,230,"DISCONNECTED", ugfx.RED) + ugfx.set_default_font(ugfx.FONT_SMALL) + connected = False + + sleep.wfi() \ No newline at end of file diff --git a/breakout/main.py b/breakout/main.py index 5829ec4..f2fbd87 100644 --- a/breakout/main.py +++ b/breakout/main.py @@ -1,9 +1,9 @@ """Breakout!""" -___name___ = "Breakout" +___title___ = "Breakout" ___license___ = "MIT" ___categories___ = ["Games"] -___dependencies___ = ["app", "ugfx_helper", "random", "buttons"] +___dependencies___ = ["app", "ugfx_helper", "buttons"] from tilda import Buttons import ugfx, ugfx_helper, dialogs diff --git a/btscan/main.py b/btscan/main.py index 787e227..bf72955 100644 --- a/btscan/main.py +++ b/btscan/main.py @@ -1,6 +1,6 @@ """Scan for and display nearby bluetooth devices""" -___name___ = "Bluetooth Scan" +___title___ = "Bluetooth Scan" ___license___ = "MIT" ___dependencies___ = ["sleep", "app", "sim800"] ___categories___ = ["Other", "System"] diff --git a/custom_image_home/main.py b/custom_image_home/main.py index b6f475a..a4877cb 100644 --- a/custom_image_home/main.py +++ b/custom_image_home/main.py @@ -3,7 +3,7 @@ Clone of the default homescreen for the Tilda Mk4. Shows the EMF homescreen and a picture loaded on the badge alternately. """ -___name___ = "Custom Image Home" +___title___ = "Custom Image Home" ___license___ = "MIT" ___categories___ = ["Homescreens"] ___dependencies___ = ["homescreen", "shared/logo.png", "shared/sponsors.png"] diff --git a/dowsingrod/main.py b/dowsingrod/main.py index 2305417..e2cc1cc 100644 --- a/dowsingrod/main.py +++ b/dowsingrod/main.py @@ -1,6 +1,6 @@ """This is a dowsing rod for WiFi APs""" -___name___ = "Dowsing Rod" +___title___ = "Dowsing Rod" ___license___ = "MIT" ___dependencies___ = ["sleep", "app", "wifi", "sim800"] ___categories___ = ["EMF", "System"] diff --git a/emfcampqueer_home/main.py b/emfcampqueer_home/main.py index 742eca4..5d753b4 100644 --- a/emfcampqueer_home/main.py +++ b/emfcampqueer_home/main.py @@ -2,7 +2,7 @@ emfcampqueer theme by ganbariley """ -___name___ = "EMFCamp Rainbow Homescreen" +___title___ = "EMFCamp Rainbow Homescreen" ___license___ = "MIT" ___categories___ = ["Homescreens"] ___dependencies___ = ["homescreen"] @@ -12,6 +12,12 @@ ___bootstrapped___ = False import ugfx from homescreen import * import time +from tilda import Buttons +from machine import Pin +from machine import Neopix + +torch = Pin(Pin.GPIO_FET) +neo = Neopix() # Padding for name intro_height = 30 @@ -26,6 +32,8 @@ logo_width = 56 # Maximum length of name before downscaling max_name = 8 +torch_on = False + # Background stuff init() ugfx.clear(ugfx.html_color(0x800080)) @@ -82,4 +90,13 @@ while True: if value_battery: text += "Battery: %s%%" % int(value_battery) status.text(text) + if Buttons.is_pressed(Buttons.BTN_Star): + if torch_on: + torch_on = False + torch.off() + neo.display([0,0]) + else: + torch_on = True + torch.on() + neo.display([0xffffff,0xffffff]) sleep_or_exit(0.5) diff --git a/enby/main.py b/enby/main.py index de07335..a68cca3 100644 --- a/enby/main.py +++ b/enby/main.py @@ -4,7 +4,7 @@ Similar to the default homescreen, but the background is the enby flag. Based on Pride Flag Homescreen by marekventur """ -___name___ = "Enby" +___title___ = "Enby" ___license___ = "MIT" ___categories___ = ["Homescreens"] ___dependencies___ = ["homescreen", "app"] diff --git a/game-of-life/main.py b/game-of-life/main.py index 7af9a36..afedcdd 100644 --- a/game-of-life/main.py +++ b/game-of-life/main.py @@ -1,9 +1,9 @@ """Game of Life""" -___name___ = "Conway game of life" +___title___ = "Conway game of life" ___license___ = "MIT" ___categories___ = ["Games"] -___dependencies___ = ["app", "ugfx_helper", "random", "sleep", "buttons"] +___dependencies___ = ["app", "ugfx_helper", "sleep", "buttons"] import app, ugfx, ugfx_helper, buttons, sleep, time, random from tilda import Buttons diff --git a/hello_world/main.py b/hello_world/main.py index 7cdd174..3afdddd 100644 --- a/hello_world/main.py +++ b/hello_world/main.py @@ -1,6 +1,6 @@ """This is a simple hello world app""" -___name___ = "Hello World" +___title___ = "Hello World" ___license___ = "MIT" ___dependencies___ = ["sleep", "app"] ___categories___ = ["EMF"] diff --git a/holland/main.py b/holland/main.py index d3a46b8..1fcfacb 100644 --- a/holland/main.py +++ b/holland/main.py @@ -1,6 +1,6 @@ -"""Camp Holland app -""" -___name___ = "Holland" +"""Camp Holland app""" + +___title___ = "Holland" ___license___ = "MIT" ___dependencies___ = ["app", "sim800", "ugfx_helper"] ___categories___ = ["Villages"] diff --git a/home_default/main.py b/home_default/main.py index e358791..263aa4d 100644 --- a/home_default/main.py +++ b/home_default/main.py @@ -5,7 +5,7 @@ It gets automatically installed when a badge is newly activated or reset. """ -___name___ = "Homescreen (Default)" +___title___ = "Homescreen (Default)" ___license___ = "MIT" ___categories___ = ["Homescreens"] ___dependencies___ = ["homescreen", "shared/logo.png", "shared/sponsors.png"] diff --git a/home_ham/main.py b/home_ham/main.py index 69dfadb..e471545 100644 --- a/home_ham/main.py +++ b/home_ham/main.py @@ -3,7 +3,7 @@ This is a modified version of the default homescreen that allows you to set a callsign """ -___name___ = "Amateur Radio Homescreen" +___title___ = "Amateur Radio Homescreen" ___license___ = "MIT" ___categories___ = ["Homescreens"] ___dependencies___ = ["homescreen"] diff --git a/home_stratum0/main.py b/home_stratum0/main.py index c39f74b..053fbbd 100644 --- a/home_stratum0/main.py +++ b/home_stratum0/main.py @@ -3,7 +3,7 @@ This is the Stratum 0 flavored homescreen for the Tilda Mk4. """ -___name___ = "Homescreen (Stratum 0)" +___title___ = "Homescreen (Stratum 0)" ___license___ = "MIT" ___categories___ = ["Homescreens"] ___dependencies___ = ["homescreen"] diff --git a/home_trans/main.py b/home_trans/main.py index 1b2bead..fe3fcbe 100644 --- a/home_trans/main.py +++ b/home_trans/main.py @@ -5,7 +5,7 @@ Press 0 to go back to normal or 8 to show the flag. Hold * to activate all LEDs for use as a torch. """ -___name___ = "Homescreen (Trans)" +___title___ = "Homescreen (Trans)" ___license___ = "MIT" ___categories___ = ["Homescreens"] ___dependencies___ = ["homescreen", "shared/logo.png"] diff --git a/launcher/main.py b/launcher/main.py index 1fa4585..a5fe98e 100644 --- a/launcher/main.py +++ b/launcher/main.py @@ -1,6 +1,6 @@ """Launcher for apps currently installed""" -___name___ = "Launcher" +___title___ = "Launcher" ___license___ = "MIT" ___categories___ = ["System"] ___dependencies___ = ["dialogs", "app", "ugfx_helper"] diff --git a/lib/dialogs.py b/lib/dialogs.py index 1d7be4e..c53c597 100644 --- a/lib/dialogs.py +++ b/lib/dialogs.py @@ -129,7 +129,7 @@ def handle_keypad(edit, numeric): buttons.Buttons.BTN_6: ["m", "n", "o", "6"], buttons.Buttons.BTN_7: ["p", "q", "r", "s", "7"], buttons.Buttons.BTN_8: ["t", "u", "v", "8"], - buttons.Buttons.BTN_9: ["w", "x", "y", "9"], + buttons.Buttons.BTN_9: ["w", "x", "y", "z", "9"], buttons.Buttons.BTN_Hash: ["#"], buttons.Buttons.BTN_Star: ["*", "+"], } @@ -183,11 +183,20 @@ def prompt_option(options, index=0, text = None, title=None, select_text="OK", n options_list = ugfx.List(5, list_y, ugfx.width() - 24, 265 - list_y, parent = window) options_list.disable_draw() + optnum = 1 for option in options: if isinstance(option, dict) and option["title"]: - options_list.add_item(option["title"]) + title = option["title"] else: - options_list.add_item(str(option)) + title = str(option) + + if optnum < 11: + # mod 10 to make 10th item numbered 0 + options_list.add_item("{}: {}".format((optnum % 10),title)) + else: + options_list.add_item(" {}".format(title)) + optnum = optnum + 1 + options_list.enable_draw() options_list.selected_index(index) diff --git a/lib/homescreen.py b/lib/homescreen.py index 8702735..5afa460 100644 --- a/lib/homescreen.py +++ b/lib/homescreen.py @@ -17,7 +17,7 @@ They also *may*: """ ___license___ = "MIT" -___dependencies___ = ["database", "buttons", "random", "app", "sleep", "ugfx_helper", "wifi", "sim800"] +___dependencies___ = ["database", "buttons", "app", "sleep", "ugfx_helper", "wifi", "sim800"] import database, ugfx, random, buttons, tilda, sleep, ugfx_helper, wifi, time, sim800 from app import App diff --git a/lib/ntp.py b/lib/ntp.py index 1c0fbdf..702ad41 100644 --- a/lib/ntp.py +++ b/lib/ntp.py @@ -12,7 +12,7 @@ import machine # (date(2000, 1, 1) - date(1900, 1, 1)).days * 24*60*60 NTP_DELTA = 3155673600 # With Mk3 Firmware an IP address string works 5%, hangs at socket.socket(..) 95%, could be a bug in 2016 upython? -NTP_HOSTS = ["ntp-gps.emf.camp", "0.emfbadge.pool.ntp.org", "1.emfbadge.pool.ntp.org", "2.emfbadge.pool.ntp.org", "3.emfbadge.pool.ntp.org"] +NTP_HOSTS = ["0.emfbadge.pool.ntp.org", "1.emfbadge.pool.ntp.org", "2.emfbadge.pool.ntp.org", "3.emfbadge.pool.ntp.org"] NTP_PORT = 123 def get_NTP_time(): diff --git a/lib/random.py b/lib/random.py deleted file mode 100644 index d072333..0000000 --- a/lib/random.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Library to generate random numbers - -Warning! Don't use this for anything important, it's probably biased -""" - -___license___ = "MIT" - -# todo: simplify this by using "urandom" -import os - -_bigrand_bytes = 10 -_bigrand_max = pow(256, _bigrand_bytes) - -def _bigrand(): - """generates a random number between 0 (incl) and _bigrand_max (excl)""" - base = 0 - for b in os.urandom(_bigrand_bytes): - base = (base << 8) + b - return base - -def random(): - """Return the next random floating point number in the range [0.0, 1.0).""" - return _bigrand() / _bigrand_max - -def randrange(start, stop=None): - """Return a randomly selected element from range(start, stop)""" - if stop is None: - stop = start - start = 0 - return start + (_bigrand() * (stop - start) // _bigrand_max) - -def randint(start, stop): - """Return a random integer N such that a <= N <= b.""" - return randrange(start, stop + 1) - -def shuffle(seq): - """Shuffle the sequence x in place.""" - l = len(seq) - for i in range(l): - j = randrange(l) - seq[i], seq[j] = seq[j], seq[i] diff --git a/lib/test_random.py b/lib/test_random.py index 5402caa..6c60dad 100644 --- a/lib/test_random.py +++ b/lib/test_random.py @@ -1,7 +1,7 @@ """Tests for random lib""" ___license___ = "MIT" -___dependencies___ = ["upip:unittest", "random"] +___dependencies___ = ["upip:unittest"] import unittest from random import * diff --git a/lobstervision/main.py b/lobstervision/main.py index 947132c..811c9ce 100644 --- a/lobstervision/main.py +++ b/lobstervision/main.py @@ -1,6 +1,6 @@ """View images from the EMF 2018 time-lapse camera """ -___name___ = "Lobster Vision" +___title___ = "Lobster Vision" ___license___ = "MIT" ___dependencies___ = ["app", "dialogs", "wifi", "buttons", "http", "ugfx_helper"] ___categories___ = ["Other"] diff --git a/lucky_melody_machine/main.py b/lucky_melody_machine/main.py index a98e1b1..6200ab1 100644 --- a/lucky_melody_machine/main.py +++ b/lucky_melody_machine/main.py @@ -2,7 +2,7 @@ Learn your personal lucky melody. """ -___name___ = "lucky_melody_machine" +___title___ = "Lucky Melody Machine" ___license___ = "WTFPL" ___dependencies___ = ["app", "buttons", "dialogs", "speaker", "sleep", "ugfx_helper"] ___categories___ = ["Sound"] diff --git a/mario/main.py b/mario/main.py index 2dcd37d..92d8173 100644 --- a/mario/main.py +++ b/mario/main.py @@ -5,7 +5,7 @@ Gracefully reboot into main menu on Menu Press. Replay Track when user pushes a button. """ -___name___ = "Mario Theme" +___title___ = "Mario Theme" ___license___ = "" ___categories___ = ["Sound"] ___dependencies___ = ["speaker", "buttons", "ugfx_helper", "app", "wifi", "http", "sleep" ] diff --git a/mass_storage/main.py b/mass_storage/main.py index aa88772..0cee259 100644 --- a/mass_storage/main.py +++ b/mass_storage/main.py @@ -1,6 +1,6 @@ """Enables mass storage mode in a safe way""" -___name___ = "Mass Storage Enabler" +___title___ = "Mass Storage Enabler" ___license___ = "MIT" ___dependencies___ = ["dialogs", "ugfx_helper"] ___categories___ = ["EMF"] diff --git a/memobadge/main.py b/memobadge/main.py index fe79a11..c797f72 100644 --- a/memobadge/main.py +++ b/memobadge/main.py @@ -1,6 +1,6 @@ """This app tests all the onboard sensors and system info""" -___name___ = "Memobadge" +___title___ = "Memobadge" ___license___ = "MIT" ___dependencies___ = ["app", "sim800", "sleep", "ugfx_helper"] ___categories___ = ["Sound"] diff --git a/nyan/main.py b/nyan/main.py new file mode 100644 index 0000000..3efca5c --- /dev/null +++ b/nyan/main.py @@ -0,0 +1,39 @@ +"""Nyan Cat Animation! Rotate the screen with 'A'.""" + +___name___ = "nyan" +___license___ = "MIT" +___dependencies___ = ["sleep", "app", "ugfx_helper", + "shared/nyan/0.png", + "shared/nyan/1.png", + "shared/nyan/2.png", + "shared/nyan/3.png", + "shared/nyan/4.png", + "shared/nyan/5.png"] + +___categories___ = ["Homescreens", "Other"] + +import ugfx_helper, os, wifi, ugfx, http, time, sleep, app +from tilda import Buttons + +# initialize screen +ugfx_helper.init() +ugfx.clear(ugfx.BLACK) + +ugfx.backlight(100) + +n = 0 +r = 270 +while True: + ugfx.display_image( 0, 90, "shared/nyan/{}.png".format(n) ) + n = (n+1) % 6 + sleep.sleep_ms(10) + + if Buttons.is_pressed(Buttons.BTN_B): + break + elif Buttons.is_pressed(Buttons.BTN_A): + r = (r + 180) % 360 + ugfx.clear(ugfx.BLACK) + ugfx.orientation(r) + +ugfx.clear() +app.restart_to_default() \ No newline at end of file diff --git a/party/main.py b/party/main.py index b6c7687..708e5a6 100644 --- a/party/main.py +++ b/party/main.py @@ -2,7 +2,7 @@ ''' ___author___ = 'Skybound - ECS' -___name___ = 'Party' +___title___ = 'Party' ___license___ = 'MIT' ___categories___ = ['LEDs'] ___bootstrapped___ = False diff --git a/phone/main.py b/phone/main.py index d6087bf..137766b 100644 --- a/phone/main.py +++ b/phone/main.py @@ -1,6 +1,6 @@ """Phone app for baic calling functions """ -___name___ = "Phone" +___title___ = "Phone" ___license___ = "MIT" ___dependencies___ = ["app", "dialogs", "sim800", "ugfx_helper"] ___categories___ = ["System"] diff --git a/pong/main.py b/pong/main.py new file mode 100644 index 0000000..a960adf --- /dev/null +++ b/pong/main.py @@ -0,0 +1,220 @@ +"""Pong!""" + +___name___ = "Pong" +___license___ = "WTFPL" +___categories___ = ["Games"] +___dependencies___ = ["dialogs", "app", "ugfx_helper", "sleep", "buttons"] + +import math, ugfx, ugfx_helper, random, sleep, buttons, time +from tilda import Buttons + +ugfx_helper.init() + +SCREEN_WIDTH = ugfx.width() +SCREEN_HEIGHT = ugfx.height() + +bgColor = ugfx.BLACK +ballColor = ugfx.html_color(0x00FF00) +paddleColor = ugfx.html_color(0x00FF00) +netColor = ugfx.html_color(0x00FF00) + +class Paddle(): + height = 6 + width = 60 + + moveSpeed = 4 + + needsRedraw = True + + def __init__(self, type): + self.type = type + + self.x = SCREEN_WIDTH/2 + self.previousX = self.x + + if type == 0: + self.y = self.height/2 + else: + self.y = SCREEN_HEIGHT - (self.height/2) + + def draw(self): + if self.needsRedraw: + ugfx.area(int(self.previousX-self.width/2),int(self.y-self.height/2),int(self.width),int(self.height),bgColor) + self.needsRedraw = False + + ugfx.area(int(self.x-self.width/2),int(self.y-self.height/2),int(self.width),int(self.height),paddleColor) + + def update(self): + if self.type == 1: + if Buttons.is_pressed(Buttons.BTN_Hash): + self.needsRedraw = True + self.previousX = self.x + self.x += self.moveSpeed + + if Buttons.is_pressed(Buttons.BTN_Star): + self.needsRedraw = True + self.previousX = self.x + self.x -= self.moveSpeed + if self.type == 0: + if Buttons.is_pressed(Buttons.BTN_3): + self.needsRedraw = True + self.previousX = self.x + self.x += self.moveSpeed + if Buttons.is_pressed(Buttons.BTN_1): + self.needsRedraw = True + self.previousX = self.x + self.x -= self.moveSpeed + + if self.x + self.width/2 > SCREEN_WIDTH: + self.x = SCREEN_WIDTH - self.width/2 + + if self.x -self.width/2 < 0: + self.x = self.width/2 + +class Ball(): + size = 10 + + x = 0 + y = 0 + + yDeathOffset = 5+3 + + def __init__(self): + self.x = random.randint(30,SCREEN_WIDTH-30) + + self.y = SCREEN_HEIGHT/2 + + self.vX = 3 + + if random.randrange(2) == 1: + self.vY = 3 + else: + self.vY = -3 + + self.previousX = self.x + self.previousY = self.y + + self.dead = False + + def draw(self): + ugfx.area(int(self.previousX-self.size/2),int(self.previousY-self.size/2),int(self.size),int(self.size),bgColor) + ugfx.area(int(self.x-self.size/2),int(self.y-self.size/2),int(self.size),int(self.size),ballColor) + + def update(self, topPaddle, bottomPaddle): + self.previousX = self.x + self.previousY = self.y + + self.x += self.vX + self.y += self.vY + + if self.x > SCREEN_WIDTH: + self.x = SCREEN_WIDTH + self.vX = -self.vX + + if self.x < 0: + self.x = 0 + self.vX = -self.vX + + if self.y > (SCREEN_HEIGHT - self.yDeathOffset): + if (self.x > (bottomPaddle.x - bottomPaddle.width/2)) and (self.x < (bottomPaddle.x + bottomPaddle.width/2)): + self.y = SCREEN_HEIGHT - self.yDeathOffset + self.vY = -self.vY + bottomPaddle.needsRedraw = True + else: + self.dead = True + + + if self.y < self.yDeathOffset: + if (self.x > (topPaddle.x - topPaddle.width/2)) and (self.x < (topPaddle.x + topPaddle.width/2)): + self.y = self.yDeathOffset + self.vY = -self.vY + topPaddle.needsRedraw = True + else: + self.dead = True + + def isDead(self): + return self.dead + +def one_round(): + ball = Ball() + topPaddle = Paddle(0) + bottomPaddle = Paddle(1) + + ugfx.clear(bgColor) + ugfx.backlight(100) + + ugfx.set_default_font(ugfx.FONT_TITLE) + + while True: + topPaddle.update() + bottomPaddle.update() + ball.update(topPaddle, bottomPaddle) + + if ball.isDead(): + if(ball.y > SCREEN_HEIGHT/2): + return [1,0] + else: + return [0,1] + + topPaddle.draw() + bottomPaddle.draw() + ball.draw() + + #draw the net + for i in range(0,7): + ugfx.area(int(i*2*SCREEN_WIDTH/13), int(SCREEN_HEIGHT/2-1), int(SCREEN_WIDTH/13), 3, netColor) + + ugfx.orientation(0) + ugfx.text(130, 0, "%d " % (points[0]),netColor) + ugfx.text(170, 0, "%d " % (points[1]),netColor) + ugfx.orientation(270) + + time.sleep_ms(1) + +minScore = 9 + +points = [0,0] +playing = 1 +while playing: + points[0] = 0 + points[1] = 0 + + while (points[0] < minScore) and (points[1] < minScore): + score = one_round() + + points[0] = points[0] + score[0] + points[1] = points[1] + score[1] + + ugfx.area(0,0,ugfx.width(),ugfx.height(),0) + + ugfx.orientation(90) + ugfx.set_default_font(ugfx.FONT_TITLE) + ugfx.text(30, 138, "GAME ",ballColor) + ugfx.text(30, 158, "OVER ",ballColor) + + ugfx.set_default_font(ugfx.FONT_SMALL) + ugfx.text(70, 220, "Score: %d - %d " % (points[0], points[1]), ballColor) + ugfx.text(36, 260, "Press A to play again ", ballColor) + ugfx.text(40, 280, "Press MENU to quit " , ballColor) + + ugfx.orientation(270) + ugfx.set_default_font(ugfx.FONT_TITLE) + ugfx.text(30, 138, "GAME ",ballColor) + ugfx.text(30, 158, "OVER ",ballColor) + + ugfx.set_default_font(ugfx.FONT_SMALL) + ugfx.text(70, 220, "Score: %d - %d " % (points[1], points[0]), ballColor) + ugfx.text(36, 260, "Press A to play again ", ballColor) + ugfx.text(40, 280, "Press MENU to quit ", ballColor) + + while True: + sleep.wfi() + if buttons.is_triggered(Buttons.BTN_A): + break + + if buttons.is_triggered(Buttons.BTN_Menu): + playing = 0 + break + +app.restart_to_default() + diff --git a/praise_horse_worship_melon/horse.gif b/praise_horse_worship_melon/horse.gif new file mode 100644 index 0000000..217d976 Binary files /dev/null and b/praise_horse_worship_melon/horse.gif differ diff --git a/praise_horse_worship_melon/loading.gif b/praise_horse_worship_melon/loading.gif new file mode 100644 index 0000000..9eedf5b Binary files /dev/null and b/praise_horse_worship_melon/loading.gif differ diff --git a/praise_horse_worship_melon/main.py b/praise_horse_worship_melon/main.py new file mode 100644 index 0000000..c3b1b20 --- /dev/null +++ b/praise_horse_worship_melon/main.py @@ -0,0 +1,58 @@ +"""Praise the Horse or Worship the Melon, directly from your badge +""" +___name___ = "Praise Horse! Worship Melon!" +___license___ = "MIT" +___dependencies___ = ["app","wifi", "buttons", "ugfx_helper"] +___categories___ = ["Other"] + +import ugfx, wifi, utime, ugfx_helper, buttons +from tilda import Buttons +import random +from app import App + + +def loading_screen(): + logo = 'praise_horse_worship_melon/loading.gif' + ugfx.area(0,0,ugfx.width(),ugfx.height(),0xFFFF) + ugfx.display_image(2,2,logo) + ugfx.set_default_font(ugfx.FONT_SMALL) + ugfx.text(60, 145, "Praise Horse (A)", ugfx.GREY) + ugfx.text(55, 305, "Worship Melon (B)", ugfx.GREY) + +def show_screen(type=None): + if type == "horse": + img = "praise_horse_worship_melon/horse.gif" + color = ugfx.RED + text = "HORSE!" + elif type == "melon": + img = "praise_horse_worship_melon/melon.gif" + color = ugfx.BLUE + text = "MELON!" + else: + return + + ugfx.area(0,0,ugfx.width(),ugfx.height(), color) + ugfx.display_image(0, 0,img) + ugfx.set_default_font(ugfx.FONT_MEDIUM_BOLD) + for y_offset in range(8): + ugfx.Label(0, 42 * y_offset, ugfx.width(), 20, text, parent=None, style=None, justification=ugfx.Label.CENTER) + utime.sleep_ms(100) + + utime.sleep_ms(1000) + loading_screen() + +def start(): + ugfx_helper.init() + loading_screen() + utime.sleep_ms(2000) + return True + +running = start() +while running: + if buttons.is_triggered(Buttons.BTN_A): + show_screen(type='horse') + if buttons.is_triggered(Buttons.BTN_B): + show_screen(type='melon') + if buttons.is_triggered(Buttons.BTN_Menu): + App("launcher").boot() + utime.sleep_ms(30) diff --git a/praise_horse_worship_melon/melon.gif b/praise_horse_worship_melon/melon.gif new file mode 100644 index 0000000..6996646 Binary files /dev/null and b/praise_horse_worship_melon/melon.gif differ diff --git a/pride/main.py b/pride/main.py index 240fa44..977adf5 100644 --- a/pride/main.py +++ b/pride/main.py @@ -4,15 +4,17 @@ Similar to the default homescreen, but the background is the pride flag. """ -___name___ = "Pride" +___title___ = "Pride" ___license___ = "MIT" ___categories___ = ["Homescreens"] -___dependencies___ = ["homescreen", "app"] +___dependencies___ = ["homescreen", "app", "buttons"] from app import restart_to_default import ugfx import homescreen +from tilda import Buttons +import buttons homescreen.init() @@ -25,56 +27,94 @@ info_height = 20 # Maximum length of name before downscaling max_name = 8 -# Orientation for other people to see -ugfx.orientation(90) +flags = { + 'LGBT': [0xE70000, 0xFF8C00, 0xFFEF00, 0x00811F, 0x0044FF, 0x760089], + 'Non-Binary': [0xFFF433, 0xFFFFFF, 0x9B59D0, 0x000000], + 'Trans': [0x5BCEFA, 0xF5A9B8, 0xFFFFFF, 0xF5A9B8, 0x5BCEFA], + 'Asexual': [0x000000, 0xA3A3A3, 0xFFFFFF, 0x800080], + 'Bisexual': [0xFF0080, 0xFF0080, 0xA349A4, 0x0000FF, 0x0000FF], + 'Pansexual': [0xFF218E, 0xFCD800, 0x0194FC] +} -# Pride flag colours -colours = [0xE70000, 0xFF8C00, 0xFFEF00, 0x00811F, 0x0044FF, 0x760089] -# Draw each "band" of colour in the flag -colour_width = ugfx.width() / len(colours) -for num, colour in enumerate(colours): - width_loc = int(num * colour_width) - ugfx.area(width_loc, 0, int(colour_width), 320, ugfx.html_color(colour)) +def draw_flag(colours): + # Orientation for other people to see + ugfx.orientation(90) -# Message to display -prefix_message = "Hi I'm" + # Draw each "band" of colour in the flag + colour_width = ugfx.width() / len(colours) + for num, colour in enumerate(colours): + width_loc = int(num * colour_width) + flag_height = ugfx.height() - (name_height + info_height) + ugfx.area(width_loc, info_height, int(colour_width), flag_height, ugfx.html_color(colour)) -ugfx.set_default_font(ugfx.FONT_NAME) -# Calc center of screen -center = (int(ugfx.width() / 2), int(ugfx.height() / 2)) -# Can't use label since the background covers the flag -ugfx.text(50, center[1] + name_height, prefix_message, ugfx.WHITE) +def draw_name(): + # Orientation for other people to see + ugfx.orientation(90) -# Process name -given_name = homescreen.name("Set your name in the settings app") -if len(given_name) <= max_name: ugfx.set_default_font(ugfx.FONT_NAME) -else: - ugfx.set_default_font(ugfx.FONT_MEDIUM_BOLD) -# Draw name -ugfx.Label(0, ugfx.height() - name_height, ugfx.width(), name_height, given_name, justification=ugfx.Label.CENTER) + + # Process name + given_name = homescreen.name("Set your name in the settings app") + if len(given_name) <= max_name: + ugfx.set_default_font(ugfx.FONT_NAME) + else: + ugfx.set_default_font(ugfx.FONT_MEDIUM_BOLD) + # Draw name + ugfx.Label(0, ugfx.height() - name_height, ugfx.width(), name_height, given_name, justification=ugfx.Label.CENTER) -# Draw for the user to see -ugfx.orientation(270) -ugfx.set_default_font(ugfx.FONT_SMALL) +def draw_user_info(): + # Draw for the user to see + ugfx.orientation(270) + # Calc width center of screen + center_width = int(ugfx.width() / 2) + ugfx.set_default_font(ugfx.FONT_SMALL) -# WiFi/Battery update loop -while True: ugfx.area(0, ugfx.height() - info_height, ugfx.width(), info_height, ugfx.WHITE) wifi_strength_value = homescreen.wifi_strength() if wifi_strength_value: wifi_message = 'WiFi: %s%%' % int(wifi_strength_value) - wifi_text = ugfx.text(center[0], ugfx.height() - info_height, wifi_message, ugfx.BLACK) + ugfx.text(center_width, ugfx.height() - info_height, wifi_message, ugfx.BLACK) battery_value = homescreen.battery() if battery_value: battery_message = 'Battery: %s%%' % int(battery_value) - battery_text = ugfx.text(0, ugfx.height() - info_height, battery_message, ugfx.BLACK) + ugfx.text(0, ugfx.height() - info_height, battery_message, ugfx.BLACK) + +# Set variables for WiFi/Battery loop +selection_change = True +flag_names = list(flags.keys()) +selection = flag_names.index('LGBT') + +# WiFi/Battery update loop +draw_name() +while True: + # Buttons will cycle when it reaches either side of the list + if buttons.is_pressed(Buttons.JOY_Left): + if selection > 0: + selection -= 1 + else: + selection = len(flags) - 1 + selection_change = True + + elif buttons.is_pressed(Buttons.JOY_Right): + if selection < len(flags) - 1: + selection += 1 + else: + selection = 0 + selection_change = True + + # Only triggers if the selection has changed + if selection_change: + draw_flag(flags[flag_names[selection]]) + selection_change = False + + # Redraw time-sensitive info on each iteration + draw_user_info() homescreen.sleep_or_exit(1.5) restart_to_default() diff --git a/pybcdc.inf b/pybcdc.inf new file mode 100644 index 0000000..2b8c09a --- /dev/null +++ b/pybcdc.inf @@ -0,0 +1,92 @@ +; Windows USB CDC ACM Setup File +; Based on INF files which were: +; Copyright (c) 2000 Microsoft Corporation +; Copyright (C) 2007 Microchip Technology Inc. +; Likely to be covered by the MLPL as found at: +; . + +[Version] +Signature="$Windows NT$" +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} +Provider=%MFGNAME% +LayoutFile=layout.inf +DriverVer=03/11/2010,5.1.2600.3 + +[Manufacturer] +%MFGNAME%=DeviceList, NTamd64 + +[DestinationDirs] +DefaultDestDir=12 + +;--------------------------------------------------------------------- +; Windows 2000/XP/Server2003/Vista/Server2008/7 - 32bit Sections + +[DriverInstall.nt] +include=mdmcpq.inf +CopyFiles=DriverCopyFiles.nt +AddReg=DriverInstall.nt.AddReg + +[DriverCopyFiles.nt] +usbser.sys,,,0x20 + +[DriverInstall.nt.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[DriverInstall.nt.Services] +AddService=usbser, 0x00000002, DriverService.nt + +[DriverService.nt] +DisplayName=%SERVICE% +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\usbser.sys + +;--------------------------------------------------------------------- +; Windows XP/Server2003/Vista/Server2008/7 - 64bit Sections + +[DriverInstall.NTamd64] +include=mdmcpq.inf +CopyFiles=DriverCopyFiles.NTamd64 +AddReg=DriverInstall.NTamd64.AddReg + +[DriverCopyFiles.NTamd64] +usbser.sys,,,0x20 + +[DriverInstall.NTamd64.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[DriverInstall.NTamd64.Services] +AddService=usbser, 0x00000002, DriverService.NTamd64 + +[DriverService.NTamd64] +DisplayName=%SERVICE% +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\usbser.sys + +;--------------------------------------------------------------------- +; Vendor and Product ID Definitions + +[SourceDisksFiles] +[SourceDisksNames] +[DeviceList] +%DESCRIPTION%=DriverInstall, USB\VID_f055&PID_9800&MI_00, USB\VID_f055&PID_9800&MI_01 + +[DeviceList.NTamd64] +%DESCRIPTION%=DriverInstall, USB\VID_f055&PID_9800&MI_00, USB\VID_f055&PID_9800&MI_01 + +;--------------------------------------------------------------------- +; String Definitions + +[Strings] +MFGFILENAME="pybcdc" +MFGNAME="Micro Python" +DESCRIPTION="Pyboard USB Comm Port" +SERVICE="USB Serial Driver" diff --git a/review_helper/main.py b/review_helper/main.py index 8a799dc..db21e2a 100644 --- a/review_helper/main.py +++ b/review_helper/main.py @@ -1,6 +1,6 @@ """Helps to test incoming PRs""" -___name___ = "PR Review Helper" +___title___ = "PR Review Helper" ___license___ = "MIT" ___categories___ = ["System"] ___dependencies___ = ["dialogs", "app", "ugfx_helper", "badge_store", "http", "stack_nav", "wifi"] diff --git a/screendisco/main.py b/screendisco/main.py index 0615914..cbea1c0 100644 --- a/screendisco/main.py +++ b/screendisco/main.py @@ -1,9 +1,9 @@ """ Flashes random colours on your screen "" By Pez (@Pezmc) """ -___name___ = "Screen Party" +___title___ = "Screen Party" ___license___ = "MIT" -___dependencies___ = ["ugfx_helper", "sleep", "random"] +___dependencies___ = ["ugfx_helper", "sleep"] ___categories___ = ["Homescreens"] ___bootstrapped___ = False diff --git a/sequencer/main.py b/sequencer/main.py index 987b95d..b9746d3 100644 --- a/sequencer/main.py +++ b/sequencer/main.py @@ -3,7 +3,7 @@ Annoy your friends! Annoy your enemies! Annoy yourself! Maybe (maybe) make music! """ -___name___ = "Sequencer" +___title___ = "Sequencer" ___license___ = "MIT" ___categories___ = ["Sound"] ___dependencies___ = ["speaker", "buttons", "ugfx_helper", "app", "shared/sequencer_info.png"] diff --git a/serendipity/main.py b/serendipity/main.py new file mode 100644 index 0000000..05c5f65 --- /dev/null +++ b/serendipity/main.py @@ -0,0 +1,32 @@ +"""Happy accidents or unplanned, fortunate discoveries.""" + +___name___ = "serendipity" +___license___ = "MIT" +___dependencies___ = ["sleep", "app", "ugfx_helper"] +___categories___ = ["Villages"] + +import ugfx_helper, os, wifi, ugfx, http, time, sleep, app +from tilda import Buttons + +# initialize screen +ugfx_helper.init() +ugfx.clear(ugfx.BLACK) + +img = [ugfx.Image("serendipity/sun.png"), + ugfx.Image("serendipity/world.png")] + +ugfx.backlight(100) + +n = 0 +ugfx.display_image( 0, 0, img[n] ) + +while True: + + if Buttons.is_pressed(Buttons.BTN_B): + break + elif Buttons.is_pressed(Buttons.BTN_A): + n = (n+1) % 2 + ugfx.display_image( 0, 0, img[n] ) + +ugfx.clear() +app.restart_to_default() \ No newline at end of file diff --git a/serendipity/sun.png b/serendipity/sun.png new file mode 100644 index 0000000..c0cc961 Binary files /dev/null and b/serendipity/sun.png differ diff --git a/serendipity/world.png b/serendipity/world.png new file mode 100644 index 0000000..38104dc Binary files /dev/null and b/serendipity/world.png differ diff --git a/settings/main.py b/settings/main.py index d7b80a4..ab344e2 100644 --- a/settings/main.py +++ b/settings/main.py @@ -11,7 +11,7 @@ Todo: """ -___name___ = "Settings" +___title___ = "Settings" ___license___ = "MIT" ___dependencies___ = ["dialogs", "ugfx_helper", "database", "app", "stack_nav", "wifi"] ___categories___ = ["System"] diff --git a/settlers/main.py b/settlers/main.py new file mode 100644 index 0000000..5dc7372 --- /dev/null +++ b/settlers/main.py @@ -0,0 +1,187 @@ +"""Settlers of Catan game board generator""" + +___name___ = "settlers" +___license___ = "MIT" +___dependencies___ = ["ugfx_helper", "sleep"] +___categories___ = ["Games"] +___bootstrapped___ = False + +import random, ugfx, ugfx_helper, math, time, buttons +from app import App, restart_to_default +from tilda import Buttons + +ugfx_helper.init() +ugfx.clear(ugfx.BLACK) + +""" +This was an experiment in drawing hexagons. Some notes: + +Screen coords are x,y values that locate pixels on the physical display: + +0,0 → → 240,0 + ↓ ↓ +0,320 → 240,320 + +Hex coords are x,y,z values that locate the relative positions of hexagons: + + 0,1,-1 +-1,1,0 ↖ ↑ ↗ 1,0,-1 + 0,0,0 +-1,0,1 ↙ ↓ ↘ 1,-1,0 + 0,-1,1 + +Converting between the two systems can be done by multiplying the x and y +coordinates against a matrix. When converting to hex coords, the z value +can be computed from the new x and y values because x + y + z must always +equal zero. + +""" + +class Hex: + # Constant matrix used to convert from hex coords to screen coords + matrix = [3.0 * 0.5, 0.0, math.sqrt(3.0) * 0.5, math.sqrt(3.0)] + + # The screen coordinate to use as the origin for hex coordinates, + # the centre of hex 0,0,0 will be at this coordinate + origin = [math.ceil(ugfx.width() / 2), math.ceil(ugfx.height() / 2)] + + # Size in pixels of the hex, from the centre point to each corner + size = 22 + + # Possible kinds of resource and the colour it should be rendered + kinds = { + 0: ugfx.html_color(0xd4e157), # Sheep + 1: ugfx.html_color(0xffc107), # Wheat + 2: ugfx.html_color(0x993300), # Wood + 3: ugfx.html_color(0xff0000), # Brick + 4: ugfx.html_color(0x757575), # Ore + 5: ugfx.html_color(0xffee55), # Desert (nothing) + } + + # Transformations for how to get to the neighbouring hexes + directions = { + 0: [-1, 1, 0], # South West + 1: [0, 1, -1], # South + 2: [1, 0, -1], # South East + 3: [1, -1, 0], # North East + 4: [0, -1, 1], # North + 5: [-1, 0, 1], # North West + } + + def __init__(self, coords, kind, number, robber): + """Create a new hex at the given hex coordinates, of the given kind of resource""" + # Validate coords + assert len(coords) == 3, 'Invalid number of hexagon coordinates' + assert coords[0] + coords[1] + coords[2] == 0, 'Invalid hexagon coordinate values' + self.coords = coords + + # The kind of resource hosted by this hex + self.kind = kind + + # The dice roll required to win this resource + self.number = number + + # Whether this hex contains the robber + self.robber = robber + + # Compute the screen coordinates of the centre of the hex + self.centre = Hex.to_screen_coords(self.coords[0], self.coords[1]) + + # Generate screen coordinates for each of the corners of the hex + self.corners = [] + for i in range(0, 6): + angle = 2.0 * math.pi * (0 - i) / 6 + offset = [Hex.size * math.cos(angle), Hex.size * math.sin(angle)] + self.corners.append([round(self.centre[0] + offset[0]), round(self.centre[1] + offset[1])]) + + @staticmethod + def to_screen_coords(x, y): + """Returns screen coordinates computed from the given hex coordinates""" + newX = (Hex.matrix[0] * x + Hex.matrix[1] * y) * Hex.size + newY = (Hex.matrix[2] * x + Hex.matrix[3] * y) * Hex.size + return [newX + Hex.origin[0], newY + Hex.origin[1]] + + @staticmethod + def get_neighbouring_hex_coords(coords, direction): + return [a + b for a, b in zip(coords, Hex.directions[direction])] + + def draw(self): + """Draw the hexagon to the screen""" + ugfx.fill_polygon(0, 0, self.corners, Hex.kinds[self.kind]) + text_offset = Hex.size * 0.5 + if self.robber: + ugfx.text(round(self.centre[0] - text_offset), round(self.centre[1] - text_offset), "Rb ", ugfx.BLACK) + else: + if self.kind != 5: + ugfx.text(round(self.centre[0] - text_offset), round(self.centre[1] - text_offset), "{} ".format(self.number), ugfx.BLACK) + + def clear(self): + ugfx.fill_polygon(0, 0, self.corners, ugfx.BLACK) + + +def board_setup(resources, numbers): + """Generate a random game board""" + + # Two rings of hexes around the centre + radius = 2 + # Choose a starting hex on the outermost ring of hexes + choice = random.randrange(0, 6) + coords = [0, 0, 0] + for i in range(radius): + coords = [a + b for a, b in zip(coords, Hex.directions[choice])] + + # Copy lists so we can edit them with impunity + r_copy = resources.copy() + n_copy = numbers.copy() + + hexes = [] + while radius > 0: + # From the starting hex, go radius hexes in each of the 6 directions + for i in list(range((choice + 2) % 6, 6)) + list(range(0, (choice + 2) % 6)): + for j in range(radius): + # The resources are picked at random from the list + resource = r_copy.pop(random.randrange(0, len(r_copy))) + # But the dice roll numbers are picked in order, unless it's + # the desert in which case that is always 7 + if resource == 5: + number = 7 + else: + number = n_copy.pop(0) + hexes.append(Hex(coords, resource, number, number == 7)) + coords = Hex.get_neighbouring_hex_coords(coords, i) + + # Go into the next ring of hexes (opposite direction of starting choice) + coords = Hex.get_neighbouring_hex_coords(coords, (choice + 3) % 6) + radius = radius - 1 + resource = r_copy.pop() + if resource == 5: + number = 7 + else: + number = n_copy.pop(0) + hexes.append(Hex(coords, resource, number, number == 7)) + return hexes + + +# List of resources (pre-randomised to combat the not-very random number +# generator) and dice rolls (these have a strict order) for 2-4 player games +resources = [4, 0, 1, 4, 4, 2, 5, 3, 2, 1, 2, 2, 1, 0, 3, 0, 3, 1, 0] +numbers = [5, 2, 6, 3, 8, 10, 9, 12, 11, 4, 8, 10, 9, 4, 5, 6, 3, 11] + +def draw(): + hexes = board_setup(resources, numbers) + for h in hexes: + h.clear() + time.sleep_ms(100) + h.draw() + +ugfx.text(5, 5, 'Press A to generate another ', ugfx.WHITE) +draw() + +# Main Loop +while True: + if buttons.is_triggered(tilda.Buttons.BTN_A): + draw() + elif buttons.is_triggered(tilda.Buttons.BTN_Menu): + break + time.sleep_ms(5) +restart_to_default() diff --git a/shared/nyan/0.png b/shared/nyan/0.png new file mode 100644 index 0000000..7a9128c Binary files /dev/null and b/shared/nyan/0.png differ diff --git a/shared/nyan/1.png b/shared/nyan/1.png new file mode 100644 index 0000000..73ce179 Binary files /dev/null and b/shared/nyan/1.png differ diff --git a/shared/nyan/2.png b/shared/nyan/2.png new file mode 100644 index 0000000..04db181 Binary files /dev/null and b/shared/nyan/2.png differ diff --git a/shared/nyan/3.png b/shared/nyan/3.png new file mode 100644 index 0000000..3589976 Binary files /dev/null and b/shared/nyan/3.png differ diff --git a/shared/nyan/4.png b/shared/nyan/4.png new file mode 100644 index 0000000..187ed61 Binary files /dev/null and b/shared/nyan/4.png differ diff --git a/shared/nyan/5.png b/shared/nyan/5.png new file mode 100644 index 0000000..6275a5a Binary files /dev/null and b/shared/nyan/5.png differ diff --git a/sms/main.py b/sms/main.py index ba61523..c1e0211 100644 --- a/sms/main.py +++ b/sms/main.py @@ -1,6 +1,6 @@ """SMS app for reading and sending messages """ -___name___ = "SMS" +___title___ = "SMS" ___license___ = "MIT" ___dependencies___ = ["app", "dialogs", "sim800", "ugfx_helper"] ___categories___ = ["System"] diff --git a/snake/main.py b/snake/main.py index 13761e8..86eb8d3 100644 --- a/snake/main.py +++ b/snake/main.py @@ -1,11 +1,11 @@ """Snake!""" -___name___ = "Snake" +___title___ = "Snake" ___license___ = "MIT" ___categories___ = ["Games"] -___dependencies___ = ["dialogs", "app", "ugfx_helper", "random", "sleep", "buttons"] +___dependencies___ = ["dialogs", "app", "ugfx_helper", "sleep", "buttons"] -import math, ugfx, ugfx_helper, random, sleep, buttons +import app, math, ugfx, ugfx_helper, random, sleep, buttons from tilda import Buttons ugfx_helper.init() diff --git a/speedlauncher/main.py b/speedlauncher/main.py new file mode 100644 index 0000000..f56174c --- /dev/null +++ b/speedlauncher/main.py @@ -0,0 +1,118 @@ +"""Launcher for apps currently installed""" + +___name___ = "Speed Launcher" +___license___ = "WTFPL" +___categories___ = ["System"] +___dependencies___ = ["app", "ugfx_helper"] +___launchable___ = False +___bootstrapped___ = False + +import ugfx_helper, ugfx, math, buttons +from app import * +from tilda import Buttons + +APPS_PER_PAGE = 12 +EMF_PURPLE = 0x800080 + +ugfx_helper.init() +ugfx.clear(ugfx.html_color(EMF_PURPLE)) + +ugfx.set_default_font(ugfx.FONT_SMALL) +style = ugfx.Style() +style.set_enabled([ugfx.WHITE, ugfx.html_color(EMF_PURPLE), ugfx.html_color(EMF_PURPLE), ugfx.html_color(EMF_PURPLE)]) +style.set_background(ugfx.html_color(EMF_PURPLE)) +ugfx.set_default_style(style) + +loadMsg = ugfx.Label(0, 90, ugfx.width(), 20, "Loading apps...", justification=ugfx.Label.CENTER) + +# Load apps in a colourList +all_apps = [{"title": a.title, "app": a} for a in get_apps()] + +# Sort apps by alphabetical order +all_apps.sort(key=lambda a: a['title']) +total_pages = math.ceil(len(all_apps) / APPS_PER_PAGE) + +ugfx.clear(ugfx.html_color(EMF_PURPLE)) + +keypad = [ + Buttons.BTN_1, + Buttons.BTN_2, + Buttons.BTN_3, + Buttons.BTN_4, + Buttons.BTN_5, + Buttons.BTN_6, + Buttons.BTN_7, + Buttons.BTN_8, + Buttons.BTN_9, + Buttons.BTN_Star, + Buttons.BTN_0, + Buttons.BTN_Hash +] + +keypadLabels = [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "*", + "0", + "#" +] + +def showPage(): + global current_page + # avoid out of bounds errors + current_page = max(1, min(current_page, total_pages)) + + start = (current_page - 1) * APPS_PER_PAGE + end = start + APPS_PER_PAGE + apps_on_current_page = all_apps[start:end] + + # Refresh page + ugfx.clear(ugfx.html_color(EMF_PURPLE)) + + # Write current page number and arrows + ugfx.Label(0, 20, ugfx.width(), 20, "Page {} of {}".format(current_page, total_pages), justification=ugfx.Label.CENTER) + + if current_page > 1: + ugfx.fill_polygon(10, 16, [[0, 10], [15, 20], [15, 0]], ugfx.WHITE) + + if current_page < total_pages: + ugfx.fill_polygon(ugfx.width() - 30, 16, [[0, 0], [15, 10], [0, 20]], ugfx.WHITE) + + # Write app numbers and names + i = 0 + yOffset = 45 + xOffset = 0 + for a in apps_on_current_page: + # xOffset = (i % 3) * 8 # offset lines to match the physical layout of the keypad + ugfx.area(20 + xOffset, yOffset + 2, 20, 20, ugfx.WHITE) + ugfx.text(23 + xOffset, yOffset + 3, keypadLabels[i] + " ", EMF_PURPLE) + + ugfx.Label(46 + xOffset, yOffset + 3, ugfx.width(), 20, a['title'], justification=ugfx.Label.LEFT) + yOffset = yOffset + 22 + i = i + 1 + + while True: + for key in keypad: + keyIndex = keypad.index(key) + if buttons.is_pressed(key) and (keyIndex < len(apps_on_current_page)): + apps_on_current_page[keyIndex]['app'].boot() + break + + if buttons.is_triggered(Buttons.JOY_Right) and (current_page is not total_pages): + current_page = current_page + 1 + return + if buttons.is_triggered(Buttons.JOY_Left) and (current_page is not 1): + current_page = current_page - 1 + return + +current_page = 1 + +while True: + showPage() diff --git a/sponsors/main.py b/sponsors/main.py index 759aec8..5f8a5b1 100644 --- a/sponsors/main.py +++ b/sponsors/main.py @@ -1,6 +1,6 @@ """A big "thank you" to all our Sponsors who made this year's badge possible!""" -___name___ = "Sponsors" +___title___ = "Sponsors" ___license___ = "MIT" ___dependencies___ = ["wifi", "http", "ugfx_helper", "sleep", "app"] ___categories___ = ["EMF"] diff --git a/square_home/main.py b/square_home/main.py index 0a50b29..f948086 100644 --- a/square_home/main.py +++ b/square_home/main.py @@ -1,6 +1,6 @@ """A home screen with squares that spin""" -___name___ = "Squares home" +___title___ = "Squares home" ___license___ = "MIT" ___dependencies___ = ["sleep", "app", "ugfx_helper", "buttons", "homescreen"] ___categories___ = ["Homescreens"] diff --git a/star_wars/main.py b/star_wars/main.py index a5a4ade..55209c8 100644 --- a/star_wars/main.py +++ b/star_wars/main.py @@ -3,7 +3,7 @@ Will play music, maybe """ -___name___ = "Play Music" +___title___ = "Star Wars Music" ___license___ = "MIT" ___categories___ = ["Sound"] ___dependencies___ = ["speaker", "shared/sw.png", "buttons"] diff --git a/stories/main.py b/stories/main.py index d019bd6..84445bf 100755 --- a/stories/main.py +++ b/stories/main.py @@ -1,6 +1,6 @@ """Read stories from twentythreemillionstories.org""" -___name___ = "twenty-three million stories" +___title___ = "twenty-three million stories" ___license___ = "MIT" ___categories___ = ["Other"] ___dependencies___ = [ "app", "dialogs", "http", "ugfx_helper", "sleep" ] diff --git a/synth/main.py b/synth/main.py index d73a822..8dfae8c 100644 --- a/synth/main.py +++ b/synth/main.py @@ -3,7 +3,7 @@ Todo: fix this, it doesn't work at at the moment """ -___name___ = "Synthesizers" +___title___ = "Synthesizers" ___license___ = "MIT" ___categories___ = ["Sound"] ___dependencies___ = ["speaker", "buttons", "ugfx_helper", "app"] diff --git a/sysinfo/main.py b/sysinfo/main.py index 54e53c4..1c32baf 100644 --- a/sysinfo/main.py +++ b/sysinfo/main.py @@ -1,6 +1,6 @@ """This app tests all the onboard sensors and system info""" -___name___ = "System Info" +___title___ = "System Info" ___license___ = "MIT" ___dependencies___ = ["sleep", "app", "sim800"] ___categories___ = ["EMF", "System"] @@ -9,6 +9,7 @@ ___bootstrapped___ = True #import ugfx, os, time, sleep, app, sim800 import ugfx, app, sim800 +import os from tilda import Buttons from tilda import Sensors from machine import ADC @@ -38,6 +39,8 @@ else: ugfx.Label(5, 185, 240, 15, simversion) +ugfx.Label(5, 215, 240, 30, "Badge firmware version:\n{}".format(os.uname().version)) + ugfx.Label(5, 300, 240, 15, "** Hold A or B or MENU to exit **") diff --git a/tildatorch/main.py b/tildatorch/main.py index 73bbcd2..5837bdd 100644 --- a/tildatorch/main.py +++ b/tildatorch/main.py @@ -1,6 +1,6 @@ """This app goes with the Torch Tutorial""" -___name___ = "Tilda Torch" +___title___ = "Tilda Torch" ___license___ = "MIT" ___dependencies___ = ["sleep", "app"] ___categories___ = ["EMF"] diff --git a/tildr/main.py b/tildr/main.py index 521095b..5006d31 100644 --- a/tildr/main.py +++ b/tildr/main.py @@ -1,6 +1,6 @@ """ Tildr Dating """ -___name___ = "Tildr Dating" +___title___ = "Tildr Dating" ___license___ = "MIT" ___dependencies___ = ["wifi", "http", "ugfx_helper", "sleep", "dialogs", "sim800", "database"] ___categories___ = ["Other"] diff --git a/tildr/profile.py b/tildr/profile.py index f4d0fac..5692d0a 100644 --- a/tildr/profile.py +++ b/tildr/profile.py @@ -4,7 +4,7 @@ import database, ujson, sim800, dialogs, http def get_profile(): profile_json = database.get("tildr_profile") if profile_json is None: - return {} + return None profile = ujson.loads(profile_json) return profile diff --git a/tinda/main.py b/tinda/main.py index 7095b8c..b1bac7e 100644 --- a/tinda/main.py +++ b/tinda/main.py @@ -1,7 +1,7 @@ """ TiNDA: A dating app for TiLDA. Find your perfect EMF match! """ -___name___ = "tinda" +___title___ = "TiNDA" ___license___ = "WTFPL" ___dependencies___ = ["app", "buttons", "database", "dialogs", "http", "sleep", "ugfx_helper"] ___categories___ = ["Other", "EMF"] diff --git a/warm_and_wet/main.py b/warm_and_wet/main.py index 40fa4d3..6665c35 100644 --- a/warm_and_wet/main.py +++ b/warm_and_wet/main.py @@ -1,6 +1,6 @@ """ """ -___name___ = "my_app" +___title___ = "Warm and Wet" ___license___ = "MIT" ___dependencies___ = ["dialogs", "ugfx_helper", "app", "sleep"] ___categories___ = ["Other"]