From 1564929388f678c5c7fd5c4f357582b5721bf326 Mon Sep 17 00:00:00 2001
From: schneider <schneider@blinkenlichts.net>
Date: Wed, 21 Aug 2019 16:49:42 +0200
Subject: [PATCH] feat(app): Add some preloads

---
 preload/apps/adventure_timer/__init__.py   | 125 +++++++++
 preload/apps/adventure_timer/metadata.json |   1 +
 preload/apps/card10_nickname/__init__.py   | 143 +++++++++++
 preload/apps/card10_nickname/metadata.json |   1 +
 preload/apps/text_reader/__init__.py       | 285 +++++++++++++++++++++
 preload/apps/text_reader/metadata.json     |   1 +
 6 files changed, 556 insertions(+)
 create mode 100644 preload/apps/adventure_timer/__init__.py
 create mode 100644 preload/apps/adventure_timer/metadata.json
 create mode 100644 preload/apps/card10_nickname/__init__.py
 create mode 100644 preload/apps/card10_nickname/metadata.json
 create mode 100644 preload/apps/text_reader/__init__.py
 create mode 100644 preload/apps/text_reader/metadata.json

diff --git a/preload/apps/adventure_timer/__init__.py b/preload/apps/adventure_timer/__init__.py
new file mode 100644
index 00000000..b1bb6a8e
--- /dev/null
+++ b/preload/apps/adventure_timer/__init__.py
@@ -0,0 +1,125 @@
+import ujson
+import os
+import display
+import utime
+import buttons
+
+CONFIG_NAME = "at-timestamp.json"
+
+
+def init():
+    if CONFIG_NAME not in os.listdir("."):
+        at_config = {"time_start": "unset"}
+        f = open(CONFIG_NAME, 'w')
+        f.write(ujson.dumps(at_config))
+        f.close()
+
+    if is_timestamp_set():
+        global menu_state
+        menu_state = 1
+        timestamp_read()
+
+
+def is_timestamp_set():
+    f = open(CONFIG_NAME, 'r')
+    c = ujson.loads(f.read())
+    f.close()
+    if c["time_start"] == "unset":
+        return False
+    else:
+        return True
+
+
+def triangle(disp, x, y, left):
+    yf = 1 if left else -1
+    scale = 6
+    disp.line(x - scale * yf, int(y + scale / 2), x, y)
+    disp.line(x, y, x, y + scale)
+    disp.line(x, y + scale, x - scale * yf, y + int(scale / 2))
+
+
+def timestamp_reset():
+    f = open(CONFIG_NAME, 'r')
+    c = ujson.loads(f.read())
+    c["time_start"] = "unset"
+    f.close()
+    f = open(CONFIG_NAME, 'w')
+    f.write(ujson.dumps(c))
+    f.close()
+
+
+def timestamp_read():
+    global time_start
+    f = open(CONFIG_NAME, 'r')
+    c = ujson.loads(f.read())
+    time_start = c["time_start"]
+    f.close()
+
+
+def timestamp_write():
+    f = open(CONFIG_NAME, 'r')
+    c = ujson.loads(f.read())
+    c["time_start"] = utime.time()
+    f.close()
+    f = open(CONFIG_NAME, 'w')
+    f.write(ujson.dumps(c))
+    f.close()
+
+
+def headline():
+    disp.print("Adventure \n Time", posy=0, fg=[0, 255, 255])
+
+
+def menu():
+    if menu_state == 0:
+        disp.print("start?", posy=40, fg=[255, 255, 255])
+        triangle(disp, 10, 66, True)
+        disp.print("start.", posx=15, posy=60, fg=[0, 255, 0])
+    elif menu_state == 1:
+        seconds = utime.time() - time_start
+        m, s = divmod(seconds, 60)
+        h, m = divmod(m, 60)
+        disp.print("%02d:%02d:%02d" % (h, m, s), posy=40, fg=[255, 255, 255])
+        triangle(disp, 10, 66, True)
+        disp.print("reset", posx=15, posy=60, fg=[255, 0, 0])
+    elif menu_state == 2:
+        disp.print("reset?", posy=40, fg=[255, 255, 255])
+        triangle(disp, 10, 66, True)
+        triangle(disp, 150, 66, False)
+        disp.print("no", posx=15, posy=60, fg=[255, 0, 0])
+        disp.print("yes", posx=105, posy=60, fg=[0, 255, 0])
+
+
+menu_state = 0
+time_start = 0
+disp = display.open()
+button_pressed = False
+init()
+
+while True:
+    disp.clear()
+    headline()
+    v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT)
+    if v == 0:
+        button_pressed = False
+    if menu_state == 0:
+        if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
+            button_pressed = True
+            timestamp_write()
+            timestamp_read()
+            menu_state = 1
+    elif menu_state == 1:
+        if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
+            button_pressed = True
+            menu_state = 2
+    elif menu_state == 2:
+        if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
+            button_pressed = True
+            menu_state = 1
+        if not button_pressed and v & buttons.BOTTOM_RIGHT != 0:
+            button_pressed = True
+            timestamp_reset()
+            menu_state = 0
+    menu()
+    disp.update()
+    utime.sleep(0.1)
diff --git a/preload/apps/adventure_timer/metadata.json b/preload/apps/adventure_timer/metadata.json
new file mode 100644
index 00000000..43aef764
--- /dev/null
+++ b/preload/apps/adventure_timer/metadata.json
@@ -0,0 +1 @@
+{"name":"adventure timer","description":"Simple stopwatch to time your current adventure.","category":"utility","author":"torben","revision":7}
\ No newline at end of file
diff --git a/preload/apps/card10_nickname/__init__.py b/preload/apps/card10_nickname/__init__.py
new file mode 100644
index 00000000..301b0906
--- /dev/null
+++ b/preload/apps/card10_nickname/__init__.py
@@ -0,0 +1,143 @@
+"""
+Improvement ideas
+- animations
+    - dvd
+    - rainbow
+    - led control
+    - fade effekt
+- led nick writing
+"""
+
+import utime
+import display
+import leds
+import ledfx
+import buttons
+import light_sensor
+import ujson
+import os
+
+FILENAME = 'nickname.txt'
+FILENAME_ADV = 'nickname.json'
+ANIM_TYPES = ['none', 'led', 'fade']
+
+
+def render_error(err1, err2):
+    with display.open() as disp:
+        disp.clear()
+        disp.print(err1, posx=80 - round(len(err1) / 2 * 14), posy=18)
+        disp.print(err2, posx=80 - round(len(err2) / 2 * 14), posy=42)
+        disp.update()
+        disp.close()
+
+
+def render_nickname(title, sub, fg, bg, fg_sub, bg_sub, main_bg):
+    anim = 'led'
+    posy = 30
+    if sub != '':
+        posy = 18
+    r = 255
+    g = 0
+    b = 0
+    while True:
+        dark = 0
+        if light_sensor.get_reading() < 30:
+            dark = 1
+        r_fg_color = fg[dark]
+        r_bg_color = bg[dark]
+        r_fg_sub_color = fg_sub[dark]
+        r_bg_sub_color = bg_sub[dark]
+        r_bg = main_bg[dark]
+        if anim == 'fade':
+            if r > 0 and b == 0:
+                r = r - 1
+                g = g + 1
+            if g > 0 and r == 0:
+                g = g - 1
+                b = b + 1
+            if b > 0 and g == 0:
+                r = r + 1
+                b = b - 1
+            r_bg = [r, g, b]
+        if anim == 'led':
+            for i in range(0, 11):
+                leds.prep(i, r_bg)
+            leds.update()
+            leds.dim_top(3)
+            leds.set_rocket(0, 15)
+            leds.set_rocket(1, 15)
+            leds.set_rocket(2, 15)
+        if anim == 'none':
+            leds.clear()
+            leds.set_rocket(0, 0)
+            leds.set_rocket(1, 0)
+            leds.set_rocket(2, 0)
+        with display.open() as disp:
+            disp.rect(0, 0, 160, 80, col=r_bg, filled=True)
+            disp.print(title, fg=r_fg_color, bg=r_bg_color, posx=80 - round(len(title) / 2 * 14), posy=posy)
+            if sub != '':
+                disp.print(sub, fg=r_fg_sub_color, bg=r_bg_sub_color, posx=80 - round(len(sub) / 2 * 14), posy=42)
+            disp.update()
+            disp.close()
+        pressed = buttons.read(
+            buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT
+        )
+        if pressed & buttons.BOTTOM_LEFT != 0:
+            anim = ANIM_TYPES[1]
+        if pressed & buttons.BOTTOM_RIGHT != 0:
+            anim = ANIM_TYPES[0]
+        utime.sleep(0.3)
+
+
+def get_key(json, key, default):
+    try:
+        return json[key]
+    except KeyError:
+        return default
+
+
+leds.clear()
+with display.open() as disp:
+    disp.clear().update()
+    disp.close()
+
+if FILENAME_ADV in os.listdir("."):
+    f = open(FILENAME_ADV, 'r')
+    try:
+        c = ujson.loads(f.read())
+        f.close()
+        # parse config
+        nick = get_key(c, 'nickname', 'no nick')
+        sub = get_key(c, 'subtitle', '')
+        # daytime values
+        background = get_key(c, 'background', [0, 0, 0])
+        fg_color = get_key(c, 'fg_color', [255, 255, 255])
+        bg_color = get_key(c, 'bg_color', background)
+        fg_sub_color = get_key(c, 'fg_sub_color', [255, 255, 255])
+        bg_sub_color = get_key(c, 'bg_sub_color', background)
+        # nighttime values
+        background_night = get_key(c, 'background_night', [0, 0, 0])
+        fg_color_night = get_key(c, 'fg_color_night', [255, 255, 255])
+        bg_color_night = get_key(c, 'bg_color_night', background_night)
+        fg_sub_color_night = get_key(c, 'fg_sub_color_night', [255, 255, 255])
+        bg_sub_color_night = get_key(c, 'bg_sub_color_night', background_night)
+        # render nickname
+        render_nickname(nick, sub, (fg_color, fg_color_night), (bg_color, bg_color_night),
+                        (fg_sub_color, fg_sub_color_night), (bg_sub_color, bg_sub_color_night),
+                        (background, background_night))
+    except ValueError:
+        render_error('invalid', 'json')
+else:
+    if FILENAME not in os.listdir("."):
+        render_error('file not', 'found')
+    else:
+        f = open(FILENAME, 'r')
+        nick = f.read()
+        f.close()
+        if len(nick) > 11:
+            render_error('name too', 'long')
+        if len(nick) < 1:
+            render_error('nick file', 'empty')
+        else:
+            render_nickname(nick, '', ([255, 255, 255], [255, 255, 255]), ([0, 0, 0], [0, 0, 0]),
+                            ([255, 255, 255], [255, 255, 255]), ([0, 0, 0], [0, 0, 0]), ([0, 0, 0], [0, 0, 0]))
diff --git a/preload/apps/card10_nickname/metadata.json b/preload/apps/card10_nickname/metadata.json
new file mode 100644
index 00000000..db70873d
--- /dev/null
+++ b/preload/apps/card10_nickname/metadata.json
@@ -0,0 +1 @@
+{"name":"Card10 Nickname","description":"Nickname app for the card10 badge\r\n\r\nEverything you need can be found here: https:\/\/github.com\/vabene1111\/card10-nickname","category":"graphics","author":"vabene1111","revision":3}
\ No newline at end of file
diff --git a/preload/apps/text_reader/__init__.py b/preload/apps/text_reader/__init__.py
new file mode 100644
index 00000000..745ecfd4
--- /dev/null
+++ b/preload/apps/text_reader/__init__.py
@@ -0,0 +1,285 @@
+"""
+Text Reader Script
+=================
+This script will list and display text files
+"""
+import buttons
+import color
+import display
+import os
+import utime
+
+STATE_LIST = "List"
+STATE_SHOW = "Show"
+SPECIAL_NO_FILES = "# no txt files"
+SPECIAL_EXIT = "[ exit ]"
+SPECIAL_EMPTY = "# empty file"
+BUTTON_TIMER_POPPED = -1
+
+def list_files():
+    """Create a list of available text files."""
+    files = sorted(os.listdir("/"))
+
+    # Filter for text files
+    files = [txt for txt in files if txt.endswith(".txt")]
+
+    return files
+
+
+def triangle(disp, x, y, left):
+    """Draw a triangle to show there's more text in this line"""
+    yf = 1 if left else -1
+    scale = 6
+    disp.line(x - scale * yf, int(y + scale / 2), x, y, col=[255,0,0])
+    disp.line(x, y, x, y + scale, col=[255,0,0])
+    disp.line(x, y + scale, x - scale * yf, y + int(scale / 2), col=[255,0,0])
+
+
+def button_events(timeout=0):
+    """Iterate over button presses (event-loop)."""
+    yield 0
+    button_pressed = False
+    count = 0
+    while True:
+        v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT)
+        if timeout > 0 and count > 0 and count % timeout == 0:
+            yield BUTTON_TIMER_POPPED
+
+        if timeout > 0:
+            count += 1
+
+        if v == 0:
+            button_pressed = False
+
+        if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
+            button_pressed = True
+            yield buttons.BOTTOM_LEFT
+
+        if not button_pressed and v & buttons.BOTTOM_RIGHT != 0:
+            button_pressed = True
+            yield buttons.BOTTOM_RIGHT
+
+        if not button_pressed and v & buttons.TOP_RIGHT != 0:
+            button_pressed = True
+            yield buttons.TOP_RIGHT
+
+        utime.sleep_ms(10)
+
+
+COLOR1, COLOR2 = (color.CHAOSBLUE_DARK, color.CHAOSBLUE)
+
+def file_len(filename):
+    i = -1
+    with open(filename) as fh:
+        for i, l in enumerate(fh):
+            pass
+    return i + 1
+
+
+def draw_filecontent(disp, filename, pos, linecount, lineoffset = 0):
+    disp.clear()
+    with open(filename) as fh:
+
+        # stop if file is empty
+        if linecount <= 0:
+            disp.print(
+                SPECIAL_EMPTY,
+                posy=20,
+                bg=color.BLACK
+            )
+            return
+
+        # calc start position
+        start = 0
+        if pos > 0:
+            start = pos-1
+        if start + 4 > linecount:
+            start = linecount - 4
+        if start < 0:
+            start = 0
+
+        # loop throuhg all lines
+        for i, line in enumerate(fh):
+            if i >= start + 4 or i >= linecount:
+                break
+            if i >= start:
+                disp.rect(
+                    0, (i - start) * 20, 159, (i - start) * 20 + 20,
+                    col=COLOR1 if i == pos else COLOR2
+                )
+
+                off = 0
+                linelength = len(line)
+                if i == pos and linelength > 11 and lineoffset > 0:
+                    off = lineoffset if lineoffset + 11 < linelength else linelength - 11
+                if lineoffset > linelength:
+                    off = 0
+
+                disp.print(
+                    line[off:(off+11)],
+                    posy=(i - start) * 20,
+                    bg=COLOR1 if i == pos else COLOR2
+                )
+                if linelength > 11 and off < linelength - 11:
+                    triangle(disp, 153, (i - start) * 20 + 6, False)
+                if off > 0:
+                    triangle(disp, 6, (i - start) * 20 + 6, True)
+
+        disp.update()
+
+
+def draw_filelist(disp, filelist, pos, filecount, lineoffset):
+    disp.clear()
+
+    start = 0
+    if pos > 0:
+        start = pos-1
+    if start + 4 > filecount:
+        start = filecount - 4
+    if start < 0:
+        start = 0
+
+    for i, line in enumerate(filelist):
+        if i >= start + 4 or i >= filecount:
+            break
+        if i >= start:
+            disp.rect(
+                0, (i - start) * 20, 159, (i - start) * 20 + 20,
+                col=COLOR1 if i == pos else COLOR2
+            )
+
+            off = 0
+            linelength = len(line)
+            if i == pos and linelength > 10 and lineoffset > 0:
+                off = lineoffset if lineoffset + 10 < linelength else linelength - 10
+            if lineoffset > linelength:
+                off = 0
+
+            disp.print(
+                " " + line[off:(off+10)],
+                posy=(i - start) * 20,
+                bg=COLOR1 if i == pos else COLOR2
+            )
+            if i == pos:
+                disp.print(">", posy=(i - start) * 20, fg=color.COMMYELLOW, bg=COLOR1)
+
+            if linelength > 10 and off < linelength - 10:
+                triangle(disp, 153, (i - start) * 20 + 6, False)
+            if off > 0:
+                triangle(disp, 24, (i - start) * 20 + 6, True)
+
+    disp.update()
+
+
+def main():
+    disp = display.open()
+    current_state = STATE_LIST
+
+    # list files variables
+
+    filelist = list_files()
+    if len(filelist) == 0:
+        filelist.append(SPECIAL_NO_FILES)
+    filelist.append(SPECIAL_EXIT)
+    numfiles = len(filelist)
+    current_file = 0
+
+    # show files variables
+
+    filename = ""
+    linecount = 0
+    linepos = 0
+    lineoffset = 0
+    lineoffdir = 0
+    timerscrollspeed = 1
+    timerstartscroll = 5
+    timercountpopped = 0
+
+    for ev in button_events(10):
+
+        # list files
+
+        if current_state == STATE_LIST:
+            if ev == buttons.BOTTOM_RIGHT:
+                # Scroll down
+                current_file = (current_file + 1) % numfiles
+                lineoffset = 0
+                timercountpopped = 0
+
+            elif ev == buttons.BOTTOM_LEFT:
+                # Scroll up
+                current_file = (current_file + numfiles - 1) % numfiles
+                lineoffset = 0
+                timercountpopped = 0
+
+            elif ev == BUTTON_TIMER_POPPED:
+                timercountpopped += 1
+                if timercountpopped >= timerstartscroll and (timercountpopped - timerstartscroll) % timerscrollspeed == 0:
+                    lineoffset += 1
+
+            elif ev == buttons.TOP_RIGHT:
+                filename = filelist [ current_file % numfiles ]
+
+                # exit or ignore
+                if filename == SPECIAL_EXIT:
+                    os.exit()
+                elif filename == SPECIAL_NO_FILES:
+                    continue
+
+                # show file, switch state and draw
+                current_state = STATE_SHOW
+                disp.clear().update()
+
+                # reset variables
+                linepos = 0
+                lineoffset = 0
+                timercountpopped = 0
+                linecount = file_len(filename)
+
+                # draw
+                draw_filecontent(disp, filename, linepos, linecount, lineoffset)
+                continue
+
+            draw_filelist(disp, filelist, current_file, numfiles, lineoffset)
+
+        # show files
+
+        elif current_state == STATE_SHOW:
+            if ev == buttons.BOTTOM_RIGHT:
+                if linepos < (linecount - 1):
+                    # Scroll down
+                    linepos += 1
+                else:
+                    # goto first line
+                    linepos = 0
+                lineoffset = 0
+                timercountpopped = 0
+
+            elif ev == buttons.BOTTOM_LEFT:
+                if linepos > 0:
+                    # Scroll up
+                    linepos -= 1
+                else:
+                    # got to last line
+                    linepos = linecount - 1
+                lineoffset = 0
+                timercountpopped = 0
+
+            elif ev == BUTTON_TIMER_POPPED:
+                timercountpopped += 1
+                if timercountpopped >= timerstartscroll and (timercountpopped - timerstartscroll) % timerscrollspeed == 0:
+                    lineoffset += 1
+
+            elif ev == buttons.TOP_RIGHT:
+                # go back to file menu
+                current_state = STATE_LIST
+                lineoffset = 0
+                timercountpopped = 0
+                draw_filelist(disp, filelist, current_file, numfiles, 0)
+                continue
+
+            draw_filecontent(disp, filename, linepos, linecount, lineoffset)
+
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/preload/apps/text_reader/metadata.json b/preload/apps/text_reader/metadata.json
new file mode 100644
index 00000000..a4132eab
--- /dev/null
+++ b/preload/apps/text_reader/metadata.json
@@ -0,0 +1 @@
+{"name":"Text Reader","description":"Provides a list of *.txt files and displays them (and let you scroll through)","category":"utility","author":"griffon","revision":2}
\ No newline at end of file
-- 
GitLab