1
0
Fork 0

rm unused apps

jebbatime
jebbatime build server 2021-06-14 11:22:11 -06:00
parent 9be8b06c68
commit df6bcefdeb
18 changed files with 0 additions and 2841 deletions

View File

@ -1,101 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
"""Analogue clock
~~~~~~~~~~~~~~~~~
Shows the time as a traditional watch face together with a battery meter.
.. figure:: res/ChronoApp.png
:width: 179
Screenshot of the analogue clock application
"""
import wasp
class ChronoApp():
"""Simple analogue clock application.
"""
NAME = 'Chrono'
def foreground(self):
"""Activate the application.
Configure the status bar, redraw the display and request a periodic
tick callback every second.
"""
wasp.system.bar.clock = False
self._draw(True)
wasp.system.request_tick(1000)
def sleep(self):
"""Prepare to enter the low power mode.
:returns: True, which tells the system manager not to automatically
switch to the default application before sleeping.
"""
return True
def wake(self):
"""Return from low power mode.
Time will have changes whilst we have been asleep so we must
udpate the display (but there is no need for a full redraw because
the display RAM is preserved during a sleep.
"""
self._draw()
def tick(self, ticks):
"""Periodic callback to update the display."""
self._draw()
def _draw(self, redraw=False):
"""Draw or lazily update the display.
The updates are as lazy by default and avoid spending time redrawing
if the time on display has not changed. However if redraw is set to
True then a full redraw is be performed.
"""
draw = wasp.watch.drawable
hi = wasp.system.theme('bright')
c1 = draw.darken(wasp.system.theme('spot1'), wasp.system.theme('contrast'))
if redraw:
now = wasp.watch.rtc.get_localtime()
# Clear the display and draw that static parts of the watch face
draw.fill()
# Redraw the status bar
wasp.system.bar.draw()
# Draw the dividers
draw.set_color(wasp.system.theme('mid'))
for theta in range(12):
draw.polar(120, 120, theta * 360 // 12, 110, 118, 3)
self._hh = 0
self._mm = 0
else:
now = wasp.system.bar.update()
if not now or self._mm == now[4]:
# Skip the update
return
# Undraw old time
hh = (30 * (self._hh % 12)) + (self._mm / 2)
mm = 6 * self._mm
draw.polar(120, 120, hh, 5, 75, 7, 0)
draw.polar(120, 120, mm, 5, 106, 5, 0)
# Record the minute that is currently being displayed
self._hh = now[3]
self._mm = now[4]
# Draw the new time
hh = (30 * (self._hh % 12)) + (self._mm / 2)
mm = 6 * self._mm
draw.polar(120, 120, hh, 5, 75, 7, hi)
draw.polar(120, 120, hh, 5, 60, 3, draw.darken(c1, 2))
draw.polar(120, 120, mm, 5, 106, 5, hi)

View File

@ -1,270 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
"""Logo demo for PineTime
~~~~~~~~~~~~~~~~~~~~~~~~~
This demo is simply an alternating sweep of the Pine64 and
MicroPython logos. It cycles through a variety of colours
and swaps between the logos every 5 images (so if you change
anything make sure len(colors) is not a multiple of 5).
"""
import wasp
import icons
# 2-bit RLE, generated from res/demo_icon.png, 292 bytes
demo_icon = (
b'\x02'
b'`@'
b'.\xc1?\x1f\xc3?\x1d\xc5?\x1b\xc7?\x19\xc9?\x17'
b'\xcb?\x16\xcc?\x10\xc1\x06\xc8\x06\xc1?\n\xc4\x06\xc4'
b'\x06\xc3?\n\xc6\x0c\xc6?\x08\xc9\x08\xc8?\x08\xc7\x0c'
b'\xc7?\x06\xc6\x06\xc4\x06\xc5?\x06\xc4\x05\xc9\x06\xc3?'
b'\x06\xc1\x06\xce\x05\xc2?\n\xd2?\x0c\xd7?\x08\xdc?'
b'\x05\xdc\x05\xc18\xc3\x05\xd7\x06\xc38\xc5\x06\xd2\x05\xc6'
b'8\xc7\x06\xce\x05\xc88\xca\x05\xc9\x06\xca8\xcc\x06\xc4'
b'\x06\xcc8\xce\x0b\xcf8\xd0\x08\xd08\xce\x0b\xcf8\xcc'
b'\x06\xc4\x06\xcc8\xc9\x06\xc9\x06\xca8\xc7\x06\xcd\x06\xc8'
b'8\xc5\x06\xd2\x06\xc58\xc3\x05\xd7\x06\xc3>\xdb\x06\xc1'
b'<\xe08\xc2\x06\xdf\x07\xc12\xc3\x06\xdb\x06\xc42\xc6'
b'\x06\xd6\x06\xc54\xc7\x06\xd1\x06\xc84\xca\x05\xcd\x06\xc9'
b'6\xcb\x06\xc8\x05\xcc6\xcd\x06\xc3\x06\xcd7\xd0\n\xcf'
b'8\xd0\x08\xd08\xce\x05\xc1\x06\xcd:\xca\x06\xc5\x06\xcb'
b':\xc8\x06\xca\x05\xc8<\xc5\x05\xcf\x06\xc5<\xc3\x05\xd3'
b'\x06\xc2?\x04\xd8?\x07\xdc?\x05\xdb?\x08\xd7?\r'
b'\xd2?\x11\xce?\x15\xc9?\x1a\xc5?\x1d\xc3?\x1e\xc3'
b'?\x1e\xc3?\x1e\xc3?\x1e\xc3?\x1e\xc3?\x1e\xc3?'
b'Q'
)
colors = (
0xffff,
0xf800, # red
0xffff,
0xffe0, # yellow
0xffff,
0x07e0, # green
0xffff,
0x07ff, # cyan
0xffff,
0x001f, # blue
0xffff,
0xf81f, # magenta
)
class Hack:
"""wasptool uses class (starting in column 0) as a clue for chunk at a
time transmission. Hence we use fake classes to demark places it is safe
to split an evaluation.
"""
pass
# 1-bit RLE, generated from res/pine64.png, 961 bytes
pine64 = (
240, 240,
b'x\x01\xee\x03\xec\x05\xea\x07\xe8\t\xe6\x0b\xe4\r\xe2\x0f'
b'\xe0\x11\xde\x13\xdc\x15\xda\x17\xd8\x19\xd6\x1b\xd4\x1d\xd2\x1f'
b"\xd1 \xcf!\xce#\xcc%\xca'\xc8)\xc6+\xc4-"
b"\xc3-\xc6'\xcb#\xd0\x1d\xb6\x02\x1d\x19\x1b\x03\x99\x05"
b'\x1e\x13\x1c\x05\x99\x08\x1d\x0f\x1c\x08\x98\n\x1e\t\x1d\n'
b'\x97\r\x1e\x05\x1c\r\x97\x10:\x10\x96\x126\x12\x95\x15'
b'1\x15\x95\x17-\x18\x93\x1b(\x1a\x93\x1d$\x1c\x93\x1e'
b'!\x1f\x91\x1d%\x1d\x91\x1b*\x1a\x91\x19.\x19\x8f\x17'
b'\x19\x01\x19\x17\x8f\x15\x19\x05\x19\x15\x8f\x13\x19\t\x1a\x13'
b'\x8d\x12\x19\r\x1a\x11\x8d\x10\x18\x12\x1a\x10\x8c\r\x19\x17'
b'\x19\x0e\x8b\x0c\x19\x1b\x19\x0c\x8b\n\x19\x1f\x1a\n\x89\t'
b'\x18$\x1a\x08\x89\x07\x18)\x19\x06\x89\x04\x19-\x19\x05'
b'\x87\x03\x191\x19\x03\x87\x01\x186\x19\x01\x9e;\xb3?'
b'\xafC\xabG\xa6L\xa2Q\x9dU\x98Z\x94^\x90c'
b'\x8bg\x87k\x85kn\x01\x18f\x1a\x01V\x03\x19a'
b'\x1a\x03V\x05\x19]\x1a\x05V\x07\x19Y\x19\x08V\n'
b'\x18T\x1a\nV\x0c\x19O\x1a\x0cV\x0e\x19K\x19\x0f'
b'V\x11\x18G\x19\x11V\x13\x18B\x1a\x13V\x16\x18='
b'\x19\x16V\x18\x189\x19\x18V\x1a\x185\x19\x1aV\x1c'
b"\x181\x19\x1cV\x1f\x18+\x19\x1fV!\x18'\x19!"
b'V#\x18#\x19#V%\x18\x1f\x18&V(\x17\x1a'
b'\x19(V*\x18\x15\x19*V,\x18\x11\x18-V/'
b'\x17\r\x18/V1\x17\x08\x191V4\x17\x03\x184'
b'V6.6V8*8V:&:V= ='
b'V<"<V:&:V7+8V505'
b'V3\x19\x01\x1a3V0\x1a\x05\x1b0V.\x19\x0b'
b"\x1a.V,\x19\x0f\x1a,V)\x1a\x13\x1b)V'"
b"\x1a\x17\x1b'V%\x19\x1d\x1a%V#\x19!\x1a#"
b'V \x1a%\x1b V\x1e\x1a)\x1b\x1eV\x1c\x1a.'
b'\x1a\x1cV\x19\x1b2\x1b\x19V\x17\x1a7\x1b\x17V\x14'
b'\x1b<\x1a\x15V\x12\x1b@\x1b\x12V\x10\x1aE\x1b\x10'
b'V\x0e\x1aI\x1b\x0eV\x0b\x1bM\x1c\x0bV\t\x1bQ'
b'\x1c\tV\x07\x1aW\x1b\x07V\x04\x1b[\x1b\x05V\x02'
b'\x1b_\x1c\x02qc\x8ai\x85m\x81q}uyz'
b'vyZ\x03\x1cu\x1d\x03=\x04\x1cq\x1d\x04>\x06'
b'\x1cm\x1d\x06?\x08\x1ch\x1c\x08@\n\x1cd\x1c\n'
b'A\x0c\x1b_\x1c\x0cB\x0e\x1b[\x1c\x0eB\x11\x1bV'
b'\x1b\x11C\x12\x1bR\x1b\x12D\x14\x1bM\x1c\x14E\x16'
b'\x1aI\x1b\x16F\x18\x1aE\x1b\x18G\x1a\x1a?\x1b\x1a'
b'H\x1c\x1a;\x1b\x1cI\x1e\x197\x1a\x1eJ \x193'
b'\x1a J"\x1a-\x1b!L$\x19)\x1a$L&'
b"\x19%\x1a&M(\x18!\x1a'N*\x19\x1c\x19*"
b'O+\x19\x18\x19+P.\x18\x13\x19.Q/\x18\x0f'
b'\x19/R2\x18\n\x182S3\x18\x05\x193T5'
b'\x18\x01\x195T8,8U9(9V<"<'
b'W< <X:$:Y7(7Z4-5'
b'Z222[/\x1a\x01\x1b/\\-\x19\x06\x1b-'
b"])\x1a\x0b\x1b)^'\x1a\x0f\x1b'_$\x1a\x13"
b'\x1b$`!\x1a\x19\x1b!a\x1e\x1a\x1d\x1b\x1eb\x1c'
b'\x1a!\x1b\x1cc\x19\x1a%\x1b\x19d\x16\x1a*\x1c\x16'
b'd\x14\x1a/\x1b\x14e\x11\x1a3\x1b\x11f\x0f\x1a7'
b'\x1b\x0fg\x0b\x1a<\x1c\x0bh\t\x1a@\x1c\ti\x06'
b'\x1aE\x1b\x06j\x03\x1bI\x1c\x03\x86M\xa0S\x9bW'
b'\x97[\x93_\x8ee\x89i\x85m\x85i\x89e\x8d`'
b'\x93[\x97W\x9bS\x9fN\xa5I\xa9E\xadA\xb1<'
b'\xb68\xbb3\xbf/\xc3+\xc7&\xcd!\xd1\x1d\xd5\x19'
b'\xda\x13\xdf\x0f\xe2\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c'
b'\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c'
b'\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c'
b'\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c\xe4\x0c'
b's'
)
class Hack:
pass
# 1-bit RLE, generated from res/micropython.png, 1491 bytes
micropython = (
240, 240,
b'\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00'
b'\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\x1fc'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!c\tc!c\tc!c\tc!c'
b'\tc!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\t-\t-'
b'\t-!-\t-\t-\t-!-\tc\t-'
b'!-\tc\t-!-\tc\t-!-\tc'
b'\t-!-\tc\t-!-\tc\t-!-'
b'\tc\t\x12\x0c\x0f!-\tc\t\x12\x0c\x0f!-'
b'\tc\t\x12\x0c\x0f!-\tc\t\x12\x0c\x0f!-'
b'\tc\t\x12\x0c\x0f!-\tc\t\x12\x0c\x0f!-'
b'\tc\t\x12\x0c\x0f!-\tc\t\x12\x0c\x0f!-'
b'\tc\t\x12\x0c\x0f!-\tc\t\x12\x0c\x0f!-'
b'\tc\t\x12\x0c\x0f!-\tc\t\x12\x0c\x0f!-'
b'\tc\t\x12\x0c\x0f!-\tc\t\x12\x0c\x0f!-'
b'\tc\t\x12\x0c\x0f!-\tc\t\x12\x0c\x0f!-'
b'\tc\t\x12\x0c\x0f!-\tc\t\x12\x0c\x0f!-'
b'\tc\t\x12\x0c\x0f!-\tc\t\x12\x0c\x0f!-'
b'\tc\t\x12\x0c\x0f!-\tc\t-!-\tc'
b'\t-!-\tc\t-!-\tc\t-!-'
b'\tc\t-!-\tc\t-!-\tc\t-'
b'!-\tc\t-!-\tc\t-!-\tc'
b'\t-!-\tc\t-!-\tc\t-!-'
b'\tc\t-!-\tc\t-!-\tc\t-'
b'!-\tc\t-!-\tc\t-!-\tc'
b'\t-\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00'
b'\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00'
b'\xff\x00\x11'
)
class DemoApp():
"""Application for live demos.
Start this to give the watch something "interesting" to do when talking
over demos!
"""
NAME = 'Demo'
ICON = demo_icon
def __init__(self):
self._logo = pine64
self._color = 0
self._i = 0
def foreground(self):
"""Draw the first frame and establish the tick."""
self._draw()
wasp.system.request_tick(2000)
def tick(self, ticks):
"""Handle the tick."""
self._draw()
wasp.system.keep_awake()
def _draw(self):
"""Draw the next frame."""
draw = wasp.watch.drawable
if self._i < 5:
self._i += 1
else:
self._i = 0
if self._logo == pine64:
self._logo = micropython
else:
self._logo = pine64
draw.fill()
draw.rleblit(self._logo, fg=colors[self._color])
self._color += 1
if self._color >= len(colors):
self._color = 0

View File

@ -1,122 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
"""Digital dual clock
~~~~~~~~~~~~~~~~~~~~~~~~
Shows a time (as HH and MM vertically) together with a battery meter.
.. figure:: res/DualApp.png
:width: 179
"""
import wasp
import icons
import fonts.clock_dual as digits
DIGITS = (
digits.clock_dual_0, digits.clock_dual_1, digits.clock_dual_2, digits.clock_dual_3,
digits.clock_dual_4, digits.clock_dual_5, digits.clock_dual_6, digits.clock_dual_7,
digits.clock_dual_8, digits.clock_dual_9
)
class DualClockApp():
"""Simple digital clock application."""
NAME = 'Dual'
# 2-bit RLE, generated from clock_dual_icon.png, 298 bytes
ICON = (
b'\x02'
b'`@'
b'?\xff\xc1@\xacF\r\xc6?\x06H\x0b\xca?\x04H'
b'\n\xcc?\x03C\x02C\n\xc4\x05\xc4?\x07C\t\xc4'
b'\x07\xc3?\x07C\t\xc3\t\xc2?\x07C\t\xc3\t\xc3'
b'?\x06C\t\xc3\t\xc3?\x06C\t\xc3\t\xc3?\x06'
b'C\t\xc4\x07\xc4?\x06C\n\xc4\x05\xc5?\x06C\n'
b'\xca\x01\xc3?\x06C\x0b\xc9\x01\xc3?\x06C\r\xc5\x03'
b'\xc3?\x06C\x15\xc3?\x06C\x14\xc4?\x06C\x14\xc3'
b'?\x07C\x14\xc3?\x07C\x13\xc4?\x07C\x0b\xc1\x05'
b'\xc5?\x03M\x06\xca?\x04M\x06\xc9?\x05M\x07\xc6'
b'?\xff\xedE\x0b\xc8?\x07I\x07\xcc?\x04K\x06\xce'
b'?\x02D\x03D\x06\xc2\x08\xc4?\x01D\x05D\x10\xc4'
b'?\x00C\x07C\x11\xc3>D\x07D\x10\xc3>C\t'
b'C\x10\xc3>C\tC\x10\xc3>C\tC\x0f\xc4>'
b'C\tC\x0e\xc4?\x00C\tC\r\xc5?\x00C\t'
b'C\x0c\xc5?\x01C\tC\x0b\xc5?\x02C\tC\n'
b'\xc5?\x03C\tC\t\xc5?\x04D\x07D\x08\xc4?'
b'\x07C\x07C\x08\xc4?\x08C\x06D\x07\xc4?\nD'
b'\x03D\x07\xc3?\x0cK\x06\xcf?\x02I\x07\xcf?\x04'
b'E\t\xcf?\xff\xff\xe4'
)
_min = None
def foreground(self):
"""Activate the application.
Configure the status bar, redraw the display and request a periodic
tick callback every second.
"""
wasp.system.bar.clock = False
self._draw(True)
wasp.system.request_tick(1000)
def sleep(self):
"""Prepare to enter the low power mode.
:returns: True, which tells the system manager not to automatically
switch to the default application before sleeping.
"""
return True
def wake(self):
"""Return from low power mode.
Time will have changes whilst we have been asleep so we must
udpate the display (but there is no need for a full redraw because
the display RAM is preserved during a sleep.
"""
self._draw()
def tick(self, ticks):
"""Periodic callback to update the display."""
self._draw()
def _draw(self, redraw=False):
"""Draw or lazily update the display.
The updates are as lazy by default and avoid spending time redrawing
if the time on display has not changed. However if redraw is set to
True then a full redraw is be performed.
"""
draw = wasp.watch.drawable
hi = wasp.system.theme('bright')
lo = wasp.system.theme('mid')
if redraw:
now = wasp.watch.rtc.get_localtime()
# Clear the display and draw that static parts of the watch face
draw.fill()
#draw.blit(digits.clock_colon, 2*48, 80, fg=mid)
# Redraw the status bar
wasp.system.bar.draw()
else:
# The update is doubly lazy... we update the status bar and if
# the status bus update reports a change in the time of day
# then we compare the minute on display to make sure we
# only update the main clock once per minute.
now = wasp.system.bar.update()
if not now or self._min == now[4]:
# Skip the update
return
# Draw the changeable parts of the watch face
draw.blit(DIGITS[now[4] % 10], 40 + 1*90, 140, fg=hi)
draw.blit(DIGITS[now[4] // 10], 40 + 0*90, 140, fg=hi)
draw.blit(DIGITS[now[3] % 10], 40 + 1*90, 40, fg=lo)
draw.blit(DIGITS[now[3] // 10], 40 + 0*90, 40, fg=lo)
#draw.roundRect(25, 135, 180, 100, 5, lo)
# Record the minute that is currently being displayed
self._min = now[4]

View File

@ -1,119 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Johannes Wache
"""Fibonacci clock
~~~~~~~~~~~~~~~~~~
The Fibonacci sequence is a sequence of numbers created by the Italian
mathematician Fibonacci in the 13th century. This is a sequence starting with
1 and 1, where each subsequent number is the sum of the previous two. For the
clock I used the first 5 terms: 1, 1, 2, 3 and 5.
.. figure:: res/FiboApp.png
:width: 179
Screenshot of the fibonacci clock application
The screen of the clock is made up of five squares whose side lengths match
the first five Fibonacci numbers: 1, 1, 2, 3 and 5. The hours are displayed
using red and the minutes using green. When a square is used to display both
the hours and minutes it turns blue. White squares are ignored.
To tell time on the Fibonacci clock you need to do some calculations. To read
the hour, simply add up the corresponding values of the red and blue squares.
To read the minutes, do the same with the green and blue squares. The minutes
are displayed in 5 minute increments (0 to 12) so you have to multiply your
result by 5 to get the actual number.
"""
import wasp
import icons
COLORS = [0xffff,0xf800,0x07e0,0x001f] # White, red, green and blue
FIELDS = b'\x05\x03\x02\x01\x01'
MONTH = 'JanFebMarAprMayJunJulAugSepOctNovDec'
# 2-bit RLE, generated from res/fibo_icon.png, 246 bytes
icon = (
b'\x02'
b'`@'
b'?\xff\xffk\xd3\x01\xc9\x01@\x1er\x10\xd3\x01\xc9\x01'
b'r\x10\xd3\x01\xc9\x01r\x10\xd3\x01\xc9\x01r\x10\xd3\x01'
b'\xc9\x01r\x10\xd3\x01\xc9\x01r\x10\xd3\x01\xc9\x01r\x10'
b'\xd3\x01\xc9\x01r\x10\xd3\x01\xc9\x01r\x10\xd3\x0br\x10'
b'\xd3\x01I\x01r\x10\xd3\x01I\x01r\x10\xd3\x01I\x01'
b'r\x10\xd3\x01I\x01r\x10\xd3\x01I\x01r\x10\xd3\x01'
b'I\x01r\x10\xd3\x01I\x01r\x10\xd3\x01I\x01r\x10'
b'\xd3\x01I\x01r.r\x10\xdd\x01r\x10\xdd\x01r\x10'
b'\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10'
b'\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10'
b'\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10'
b'\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10'
b'\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10'
b'\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10'
b'\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r\x10\xdd\x01r?'
b'\xff\xffk'
)
class FibonacciClockApp():
"""Displays the time as a Fibonacci Clock.
"""
NAME = 'Fibo'
ICON = icon
def foreground(self):
"""Activate the application."""
wasp.system.bar.clock = False
self._draw(True)
wasp.system.request_tick(1000)
def sleep(self):
return True
def wake(self):
self._draw()
def tick(self, ticks):
self._draw()
def _draw(self, redraw=False):
"""Draw or lazily update the display."""
draw = wasp.watch.drawable
if redraw:
now = wasp.watch.rtc.get_localtime()
draw.fill()
wasp.system.bar.draw()
else:
now = wasp.system.bar.update()
if not now or self._min == now[4]:
return
#calculate colors of fields:
field_colors = bytearray(5)
hr = now[3]
mn = now[4] // 5 # Clock can only display every 5 minutes
if (hr >= 12):
hr -= 12
for i in range(5):
if ((hr - FIELDS[i]) >= 0):
hr -= FIELDS[i]
field_colors[i] += 1
if ((mn - FIELDS[i]) >= 0):
mn -= FIELDS[i]
field_colors[i] += 2
draw.fill(x=71,y=60,w=23,h=23,bg=COLORS[field_colors[4]]) # 1 field
draw.fill(x=71,y=85,w=23,h=23,bg=COLORS[field_colors[3]]) # 1 field
draw.fill(x=21,y=60,w=48,h=48,bg=COLORS[field_colors[2]]) # 2 field
draw.fill(x=21,y=110,w=73,h=73,bg=COLORS[field_colors[1]]) # 3 field
draw.fill(x=96,y=60,w=123,h=123,bg=COLORS[field_colors[0]]) # 5 field
month = now[1] - 1
month = MONTH[month*3:(month+1)*3]
draw.string('{} {} {}'.format(now[2], month, now[0]),
0, 202, width=240)
# Record the minute that is currently being displayed
self._min = now[4]

View File

@ -1,258 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
"""Conway's Game of Life
~~~~~~~~~~~~~~~~~~~~~~~~
The Game of Life is a "no player game" played on a two dimensional grid
where the rules interact to make interesting patterns.
.. figure:: res/LifeApp.png
:width: 179
Screenshot of the Game of Life application
The game is based on four simple rules:
1. Death by isolation: a cell dies if has fewer than two live neighbours.
2. Death by overcrowding: a cell dies if it has more than three live
neighbours.
3. Survival: a living cell continues to survive if it has two or three
neighbours.
4. Reproduction: a dead cell comes alive if it has exactly three
neighbours.
On 11 April 2020 John H. Conway who, among many, many other
achievements, devised the rule set for his Game of Life, died of
complications from a COVID-19 infection.
The Game of Life is the first "toy" program I ever recall seeing on a
computer (running in a mid 1980s Apple Macintosh). It sparked something
even if "toy" is perhaps an underwhelming description of the Game of Life.
Either way it occupies a special place in my childhood. For that, this
application is dedicated to Professor Conway.
"""
import array
import machine
import micropython
import wasp
@micropython.viper
def xorshift12(v: int) -> int:
"""12-bit xorshift pseudo random number generator.
With only 12-bits of state this PRNG is another toy! It appears
here because it allows us to visit every possible 12-bit value
(except zero) whilst taking an interesting route. This allows us to
make the redraw (which is too slow to fully conceal) visually
engaging.
"""
v ^= v << 1
v ^= (v >> 3) & 0x1ff
v ^= (v << 7)
return v & 0xfff
@micropython.viper
def get_color(v: int) -> int:
"""Convert a 12-bit number into a reasonably bright RGB565 pixel"""
rgb = v ^ (v << 4)
while 0 == (rgb & 0xc710):
rgb += 0x2104
return rgb
@micropython.viper
def get_cell(board, stride: int, x: int, y: int) -> bool:
b = ptr32(board)
xw = x >> 5
xb = x & 0x1f
yw = y * (stride >> 5)
return bool(b[yw + xw] & (1 << xb))
@micropython.viper
def set_cell(board, stride: int, x: int, y: int, v: bool):
b = ptr32(board)
xw = x >> 5
xb = x & 0x1f
yw = y * (stride >> 5)
m = 1 << xb
c = b[yw + xw]
# viper doesn't implement bitwise not so we are having
# to clear bits using xor...
if v:
b[yw + xw] = c | m
elif c & m:
b[yw + xw] = c ^ m
@micropython.viper
def game_of_life(b, xmax: int, ymax: int, nb):
"""Run a single generation of Conway's Game of Life
In the code below we have simplified the rules described at the top
of this file to "a cell is alive it has three live neighbours or if
it was previously alive and has two neighbours, otherwise it is
dead.".
"""
board = ptr32(b)
next_board = ptr32(nb)
for y in range(1, ymax-1):
tm = int(get_cell(board, xmax, 0, y-1))
tr = int(get_cell(board, xmax, 1, y-1))
cm = int(get_cell(board, xmax, 0, y))
cr = int(get_cell(board, xmax, 1, y))
bm = int(get_cell(board, xmax, 0, y+1))
br = int(get_cell(board, xmax, 1, y+1))
for x in range(1, xmax-1):
tl = tm
tm = tr
tr = int(get_cell(board, xmax, x+1, y-1))
cl = cm
cm = cr
cr = int(get_cell(board, xmax, x+1, y))
bl = bm
bm = br
br = int(get_cell(board, xmax, x+1, y+1))
c = tl + tm + tr + cl + cr + bl + bm + br
set_cell(next_board, xmax, x, y, c == 3 or (cm and c == 2))
# 2-bit RLE, generated from res/gameoflife.png, 404 bytes
# The icon is a carefully selected generation of an "acorn", I wanted
# to avoid using a glider, they are overused to the point of cliche!
icon = (
b'\x02'
b'`@'
b'?\xff\xff\xee@\xf8B\x02B\x02B?\x16L?\x15'
b'L?\x16B\x02B\x02B?\x1bB?\x1eD?\x1d'
b'D?\x1eB?\x17\x80\xee\x82\x02\x82\x06\x82\x02\x82?'
b'\x0e\x88\x04\x88?\r\x88\x04\x88?\x0e\x82\x02\x82\x06B'
b'\x02\x82?\x03\xc0\x89\xc2\x02\xc2\x02\xc2\x02\xc2\x02\xc2\x02'
b'\xc2\x02\xc2\x02\xc2\x02\xc2\x02\xc2\x02\xc25\xec4\xec5'
b'\xc2\x02\xc2\x02\xc2\x02\xc2\x02\xc2\x02\xc2\x02\xc2\x02\xc2\x02'
b'\xc2\x02\xc2\x02\xc2*B\x02B\x12\xc2\x06B\x06\xc2\x12'
b'B\x02B\x1dH\x10\xc4\x04D\x04\xc4\x10H\x1cH\x10'
b'\xc4\x04D\x04\xc4\x10H\x1dB\x02B\x12\xc2\x06B\x06'
b'\xc2\x12B\x02B\x1eB\x16\xc2\x0e\xc2\x16B\x1dD\x14'
b'\xc4\x0c\xc4\x14D\x1cD\x14\xc4\x0c\xc4\x14D\x1dB\x16'
b'\xc2\x0e\xc2\x16B\x1eB>B\x1dD<D\x1cD<'
b'D\x1dB>B"B\x02B\x06B\x02B\x02B\x0e'
b'B\x02B\x02B\x06B\x02B%H\x04L\x0cL\x04'
b'H$H\x04L\x0cL\x04H%B\x02B\x06B\x02'
b'B\x02B\x0eB\x02B\x02B\x06B\x02B2B\n'
b'B\x06B\nB=D\x08D\x04D\x08D<D\x08'
b'D\x04D\x08D=B\nB\x06B\nB>B\x02'
b'B\x06\x82\x06\x82\x06B\x02B=H\x04\x84\x04\x84\x04'
b'H<H\x04\x84\x04\x84\x04H=B\x02B\x06\x82\x06'
b'\x82\x06B\x02B>\x82\x02\x82\x16\x82\x02\x82=\x88\x14'
b'\x88<\x88\x14\x88=\x82\x02\x82\x16\x82\x02\x82>\x82\x02'
b'\x82\x02\x82\x0e\x82\x02\x82\x02\x82=\x8c\x0c\x8c<\x8c\x0c'
b'\x8c=\x82\x02\x82\x02\x82\x0e\x82\x02\x82\x02\x82?\xff\xff'
b'\xe2'
)
class GameOfLifeApp():
"""Application implementing Conway's Game of Life.
"""
NAME = 'Life'
ICON = icon
def __init__(self):
"""Initialize the application."""
self._board = array.array('I', [0] * (64*64//32))
self._next_board = array.array('I', self._board)
self._color = 1
self.touch(None)
def foreground(self):
"""Activate the application."""
self._draw()
wasp.system.request_event(wasp.EventMask.TOUCH)
wasp.system.request_tick(625)
def tick(self, ticks):
"""Notify the application that its periodic tick is due."""
wasp.system.keep_awake()
#t = machine.Timer(id=1, period=8000000)
#t.start()
game_of_life(self._board, 64, 64, self._next_board)
#t1 = t.time()
self._update()
#t2 = t.time()
#t.stop()
#del t
#wasp.watch.drawable.string('{:4.2f}s {:4.2f}s'.format(t1 / 1000000,
# t2 / 1000000), 6, 210)
def touch(self, event):
"""Notify the application of a touchscreen touch event."""
board = self._next_board
for i in range(len(board)):
board[i] = 0
board[62] = 32 << 16
board[64] = 8 << 16
board[66] = 103 << 16
if None != event:
self._update()
def _draw(self):
"""Draw the display from scratch."""
wasp.watch.drawable.fill()
board = self._board
for i in range(len(board)):
board[i] = 0
self._update()
def _update(self):
"""Update the dynamic parts of the application display."""
b = self._board
nb = self._next_board
self._board = nb
self._next_board = b
display = wasp.watch.display
lb = display.linebuffer
alive = lb[0:2*16]
self._color = xorshift12(self._color)
rgbhi = get_color(self._color)
rgblo = rgbhi & 0xff
rgbhi >>= 8
for i in range(0, len(alive), 2):
alive[i] = rgbhi
alive[i+1] = rgblo
for i in (0, 3, 12, 15):
alive[i*2] = 0
alive[i*2+1] = 0
dead = lb[2*16:4*16]
for i in range(len(dead)):
dead[i] = 0
def draw_cell(cell, display, px):
x = ((cell & 0x3f) - 2) * 4
y = ((cell >> 6) -2) * 4
if x < 0 or x >= 240 or y < 0 or y >= 240:
return
display.set_window(x, y, 4, 4)
display.write_data(px)
draw_cell(1, display, alive if b[1//32] & (1 << (1 & 0x1f)) else dead)
v = xorshift12(1)
while 1 != v:
me = b[v//32] & (1 << (v & 0x1f))
nx = nb[v//32] & (1 << (v & 0x1f))
if me != nx:
draw_cell(v, display, alive if nx else dead)
v = xorshift12(v)
draw_cell(0, display, alive if b[0//32] & (1 << (0 & 0x1f)) else dead)

View File

@ -1,65 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
"""Haiku viewer
~~~~~~~~~~~~~~~
These three lines poems are fun to write and fit nicely on a tiny screen.
.. figure:: res/HaikuApp.png
:width: 179
If there is a file called haiku.txt in the flash filesystem then this app
allows it to be displayed three lines at a time using the pager.
This application also (optionally) loads an icon from the filesystem allowing
to be customized to match whether theme your verses are based around.
"""
import wasp
import icons
import io
import sys
from apps.pager import PagerApp
class HaikuApp(PagerApp):
NAME = 'Haiku'
def __init__(self):
# Throw an exception if there is no poetry for us to read...
open('haiku.txt').close()
try:
with open('haiku.rle', 'rb') as f:
self.ICON = f.read()
except:
# Leave the default app icon if none is present
pass
super().__init__('')
self._counter = -4
def foreground(self):
lines = []
self._counter += 4
with open('haiku.txt') as f:
for i in range(self._counter):
_ = f.readline()
lines = [ '', ]
for i in range(3):
lines.append(f.readline())
if len(lines[2]) == 0:
self._counter = 0
f.seek(0)
lines = [ '', ]
for i in range(3):
lines.append(f.readline())
self._msg = '\n'.join(lines)
super().foreground()

View File

@ -1,127 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
"""Heart rate monitor
~~~~~~~~~~~~~~~~~~~~~
A graphing heart rate monitor using a PPG sensor.
.. figure:: res/HeartApp.png
:width: 179
This program also implements some (entirely optional) debug features to
store the raw heart data to the filesystem so that the samples can be used
to further refine the heart rate detection algorithm.
To enable the logging feature select the heart rate application using the
watch UI and then run the following command via wasptool:
.. code-block:: sh
./tools/wasptool --eval 'wasp.system.app.debug = True'
Once debug has been enabled then the watch will automatically log heart
rate data whenever the heart rate application is running (and only
when it is running). Setting the debug flag to False will disable the
logging when the heart rate monitor next exits.
Finally to download the logs for analysis try:
.. code-block:: sh
./tools/wasptool --pull hrs.data
"""
import wasp
import machine
import ppg
class HeartApp():
"""Heart rate monitor application."""
NAME = 'Heart'
def __init__(self):
self._debug = False
self._hrdata = None
def foreground(self):
"""Activate the application."""
wasp.watch.hrs.enable()
# There is no delay after the enable because the redraw should
# take long enough it is not needed
draw = wasp.watch.drawable
draw.fill()
draw.set_color(wasp.system.theme('bright'))
draw.string('PPG graph', 0, 6, width=240)
wasp.system.request_tick(1000 // 8)
self._hrdata = ppg.PPG(wasp.watch.hrs.read_hrs())
if self._debug:
self._hrdata.enable_debug()
self._x = 0
def background(self):
wasp.watch.hrs.disable()
self._hrdata = None
def _subtick(self, ticks):
"""Notify the application that its periodic tick is due."""
draw = wasp.watch.drawable
spl = self._hrdata.preprocess(wasp.watch.hrs.read_hrs())
if len(self._hrdata.data) >= 240:
draw.set_color(wasp.system.theme('bright'))
draw.string('{} bpm'.format(self._hrdata.get_heart_rate()),
0, 6, width=240)
# Graph is orange by default...
color = wasp.system.theme('spot1')
# If the maths goes wrong lets show it in the chart!
if spl > 100 or spl < -100:
color = 0xffff
if spl > 104 or spl < -104:
spl = 0
spl += 104
x = self._x
draw.fill(0, x, 32, 1, 208-spl)
draw.fill(color, x, 239-spl, 1, spl)
x += 2
if x >= 240:
x = 0
self._x = x
def tick(self, ticks):
"""This is an outrageous hack but, at present, the RTC can only
wake us up every 125ms so we implement sub-ticks using a regular
timer to ensure we can read the sensor at 24Hz.
"""
t = machine.Timer(id=1, period=8000000)
t.start()
self._subtick(1)
wasp.system.keep_awake()
while t.time() < 41666:
pass
self._subtick(1)
while t.time() < 83332:
pass
self._subtick(1)
t.stop()
del t
@property
def debug(self):
return self._debug
@debug.setter
def debug(self, value):
self._debug = value
if value and self._hrdata:
self._hrdata.enable_debug()

View File

@ -1,19 +0,0 @@
# SPDX-License-Identifier: MY-LICENSE
# Copyright (C) YEAR(S), AUTHOR
import wasp
class HelloApp():
"""A hello world application for wasp-os."""
NAME = "Hello"
def __init__(self, msg="Hello, world!"):
self.msg = msg
def foreground(self):
self._draw()
def _draw(self):
draw = wasp.watch.drawable
draw.fill()
draw.string(self.msg, 0, 108, width=240)

View File

@ -1,211 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
# Copyright (C) 2020 Carlos Gil
"""Music Player for GadgetBridge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. figure:: res/MusicApp.png
:width: 179
Screenshot of the Music Player application
Music Player Controller:
* Touch: play/pause
* Swipe UPDOWN: Volume down/up
* Swipe LEFTRIGHT: next/previous
"""
import wasp
import icons
import time
from micropython import const
# 2-bit RLE, generated from res/music_icon.png, 358 bytes
icon = (
b'\x02'
b'`@'
b'\x1e\xa4<\xa4<\xa4;\xa6?X\xec2\xf0/\xf2-'
b'\xf4,\xc3@[n\xc3,\xc3n\xc3,\xc3n\xc3,'
b'\xc3n\xc3,\xc3b\xc7E\xc3,\xc3]\xccE\xc3,'
b'\xc3Z\xcfE\xc3,\xc3T\xd5E\xc3,\xc3Q\xd8E'
b'\xc3,\xc3Q\xd8E\xc3,\xc3Q\xd8E\xc3,\xc3Q'
b'\xd0E\xc3E\xc3,\xc3Q\xccI\xc3E\xc3,\xc3Q'
b'\xc9L\xc3E\xc3,\xc3Q\xc5P\xc3E\xc3,\xc3Q'
b'\xc3R\xc3E\xc3,\xc3Q\xc3R\xc3E\xc3,\xc3Q'
b'\xc3R\xc3E\xc3+\xc4Q\xc3R\xc3E\xc3*\xc5Q'
b'\xc3R\xc3E\xc3*\xc5Q\xc3R\xc3E\xc3*\xc5Q'
b'\xc3R\xc3E\xc3*\xc5Q\xc3R\xc3E\xc3*\xc5Q'
b'\xc3R\xc3E\xc3*\xc5Q\xc3R\xc3E\xc3*\xc5Q'
b'\xc3L\xc4B\xc3E\xc3+\xc4Q\xc3I\xccE\xc3,'
b'\xc3Q\xc3H\xcdE\xc3,\xc3Q\xc3G\xceE\xc3,'
b'\xc3K\xc3C\xc3F\xcfE\xc3,\xc3H\xccF\xcfE'
b'\xc3,\xc3G\xcdF\xcfE\xc3,\xc3F\xceF\xcfE'
b'\xc3,\xc3E\xcfF\xcfE\xc3,\xc3E\xcfG\xceE'
b'\xc3,\xc3D\xd0H\xccF\xc3,\xc3D\xd0I\xcaG'
b'\xc3,\xc3E\xcfK\xc5J\xc3,\xc3E\xce[\xc3,'
b'\xc3F\xcd[\xc3,\xc3G\xca]\xc3,\xc3I\xc7^'
b'\xc3,\xc3n\xc3,\xc3n\xc3,\xc3n\xc3,\xc3n'
b'\xc3,\xf4-\xf2/\xf02\xec?X\xa6;\xa4<\xa4'
b'<\xa4\x1e'
)
DISPLAY_WIDTH = const(240)
ICON_SIZE = const(72)
CENTER_AT = const((DISPLAY_WIDTH - ICON_SIZE) // 2)
class MusicPlayerApp(object):
""" Music Player Controller application."""
NAME = 'Music'
ICON = icon
def __init__(self):
self._pauseplay = wasp.widgets.GfxButton(CENTER_AT, CENTER_AT, icons.play)
self._back = wasp.widgets.GfxButton(0, 120-12, icons.back)
self._fwd = wasp.widgets.GfxButton(240-48, 120-12, icons.fwd)
self._play_state = False
self._musicstate = 'pause'
self._artist = ''
self._track = ''
self._state_changed = True
self._track_changed = True
self._artist_changed = True
def _send_cmd(self, cmd):
print('\r')
for i in range(1):
for i in range(0, len(cmd), 20):
print(cmd[i: i + 20], end='')
time.sleep(0.2)
print(' ')
print(' ')
def _fill_space(self, key):
if key == 'top':
wasp.watch.drawable.fill(
x=0, y=0, w=DISPLAY_WIDTH, h=CENTER_AT)
elif key == 'down':
wasp.watch.drawable.fill(x=0, y=CENTER_AT + ICON_SIZE,
w=DISPLAY_WIDTH,
h=DISPLAY_WIDTH - (CENTER_AT + ICON_SIZE))
def foreground(self):
"""Activate the application."""
state = wasp.system.musicstate.get('state')
artist = wasp.system.musicinfo.get('artist')
track = wasp.system.musicinfo.get('track')
if state:
self._musicstate = state
if self._musicstate == 'play':
self._play_state = True
elif self._musicstate == 'pause':
self._play_state = False
if artist:
self._artist = artist
if track:
self._track = track
wasp.watch.drawable.fill()
self.draw()
wasp.system.request_tick(1000)
wasp.system.request_event(wasp.EventMask.SWIPE_UPDOWN |
wasp.EventMask.TOUCH)
def background(self):
"""De-activate the application (without losing state)."""
self._state_changed = True
self._track_changed = True
self._artist_changed = True
def tick(self, ticks):
wasp.system.keep_awake()
music_state_now = wasp.system.musicstate.get('state')
music_artist_now = wasp.system.musicinfo.get('artist')
music_track_now = wasp.system.musicinfo.get('track')
if music_state_now:
if music_state_now != self._musicstate:
self._musicstate = music_state_now
self._state_changed = True
else:
self._state_changed = False
wasp.system.musicstate = {}
if music_track_now:
if music_track_now != self._track:
self._track = music_track_now
self._track_changed = True
else:
self._track_changed = False
if music_artist_now:
if music_artist_now != self._artist:
self._artist = music_artist_now
self._artist_changed = True
else:
self._artist_changed = False
wasp.system.musicinfo = {}
self._update()
def swipe(self, event):
"""
Notify the application of a touchscreen swipe event.
"""
if event[0] == wasp.EventType.UP:
self._send_cmd('{"t":"music", "n":"volumeup"} ')
elif event[0] == wasp.EventType.DOWN:
self._send_cmd('{"t":"music", "n":"volumedown"} ')
def touch(self, event):
if self._pauseplay.touch(event):
self._play_state = not self._play_state
if self._play_state:
self._musicstate = 'play'
self._pauseplay.gfx = icons.pause
self._pauseplay.draw()
self._send_cmd('{"t":"music", "n":"play"} ')
else:
self._musicstate = 'pause'
self._pauseplay.gfx = icons.play
self._pauseplay.draw()
self._send_cmd('{"t":"music", "n":"pause"} ')
elif self._back.touch(event):
self._send_cmd('{"t":"music", "n":"previous"} ')
elif self._fwd.touch(event):
self._send_cmd('{"t":"music", "n":"next"} ')
def draw(self):
"""Redraw the display from scratch."""
self._draw()
def _draw(self):
"""Redraw the updated zones."""
if self._state_changed:
self._pauseplay.draw()
if self._track_changed:
self._draw_label(self._track, 24 + 144)
if self._artist_changed:
self._draw_label(self._artist, 12)
self._back.draw()
self._fwd.draw()
def _draw_label(self, label, pos):
"""Redraw label info"""
if label:
draw = wasp.watch.drawable
chunks = draw.wrap(label, 240)
self._fill_space(pos)
for i in range(len(chunks)-1):
sub = label[chunks[i]:chunks[i+1]].rstrip()
draw.string(sub, 0, pos + 24 * i, 240)
def _update(self):
if self._musicstate == 'play':
self._play_state = True
self._pauseplay.gfx = icons.pause
elif self._musicstate == 'pause':
self._play_state = False
self._pauseplay.gfx = icons.play
self._draw()
def update(self):
pass

View File

@ -1,217 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Miguel Rochefort
"""Play 2048
~~~~~~~~~~~~
A popular sliding block puzzle game in which tiles are combined to make the
number 2048.
.. figure:: res/2048App.png
:width: 179
Screenshot of the 2048 game application
"""
import wasp
import icons
import widgets
import random
import fonts
from micropython import const
SCREEN_SIZE = const(240)
GRID_PADDING = const(8)
GRID_SIZE = const(4)
CELL_SIZE = const(50)
GRID_BACKGROUND = const(0x942F)
CELL_BACKGROUND = [0x9CB1, 0xEF3B, 0xEF19, 0xF58F, 0xF4AC, 0xF3EB, 0xF2E7, 0xEE6E, 0xEE6C, 0xEE4A, 0xEE27, 0xEE05]
CELL_FOREGROUND = [0x9CB1, 0x736C, 0x736C, 0xFFBE, 0xFFBE, 0xFFBE, 0xFFBE, 0xFFBE, 0xFFBE, 0xFFBE, 0xFFBE, 0xFFBE]
CELL_LABEL = ['','2','4','8','16','32','64','128','256','512','1K','2K'] # TODO: Display 1024 and 2048 (text-wrapping)
# 2-bit RLE, generated from res/2048_icon.png, 578 bytes
icon = (
b'\x02'
b'`@'
b'\x10\xbf\x01 \xbf\x01 \xbf\x01 \x83@\x81M\x82M'
b'\x82M\x82\x80\xc8\x8d\xc0\xdb\xc3 \xc3M\xc2M\xc2M'
b'\xc2\x8d\xc3 \xc3M\xc2M\xc2M\xc2\x8d\xc3 \xc3M'
b'\xc2M\xc2M\xc2\x8d\xc3 \xc3M\xc2M\xc2M\xc2\x8d'
b'\xc3 \xc3M\xc2M\xc2M\xc2\x8d\xc3 \xc3M\xc2M'
b'\xc2M\xc2\x8d\xc3 \xc3M\xc2M\xc2M\xc2\x8d\xc3 '
b'\xc3M\xc2M\xc2M\xc2\x8d\xc3 \xc3M\xc2M\xc2M'
b'\xc2\x8d\xc3 \xc3M\xc2M\xc2M\xc2\x8d\xc3 \xc3M'
b'\xc2M\xc2M\xc2\x8d\xc3 \xc3M\xc2M\xc2M\xc2\x8d'
b'\xc3 \xff\x01 \xff\x01 \xc3M\xc2M\xc2M\xc2@'
b'\xfaM\xc3 \xc3\x80\x81\x8d\xc2\x8d\xc2\x8d\xc2M\xc3 '
b'\xc3\x8d\xc2\x8d\xc2\x8d\xc2M\xc3 \xc3\x8d\xc2\x8d\xc2\x8d'
b'\xc2M\xc3 \xc3\x8d\xc2\x8d\xc2\x8d\xc2M\xc3 \xc3\x8d'
b'\xc2\x8d\xc2\x8d\xc2M\xc3 \xc3\x8d\xc2\x8d\xc2\x8d\xc2M'
b'\xc3 \xc3\x8d\xc2\x8d\xc2\x8d\xc2M\xc3 \xc3\x8d\xc2\x8d'
b'\xc2\x8d\xc2M\xc3 \xc3\x8d\xc2\x8d\xc2\x8d\xc2M\xc3 '
b'\xc3\x8d\xc2\x8d\xc2\x8d\xc2M\xc3 \xc3\x8d\xc2\x8d\xc2\x8d'
b'\xc2M\xc3 \xc3\x8d\xc2\x8d\xc2\x8d\xc2M\xc3 \xff\x01'
b' \xff\x01 \xc3\xc0\xfb\xcd@\xdbB\x8dB\x8dB\xcd'
b'C C\xcdB\x8dB\x8dB\xcdC C\xcdB\x8d'
b'B\x8dB\xcdC C\xcdB\x8dB\x8dB\xcdC '
b'C\xcdB\x8dB\x8dB\xcdC C\xcdB\x8dB\x8d'
b'B\xcdC C\xcdB\x8dB\x8dB\xcdC C\xcd'
b'B\x8dB\x8dB\xcdC C\xcdB\x8dB\x8dB\xcd'
b'C C\xcdB\x8dB\x8dB\xcdC C\xcdB\x8d'
b'B\x8dB\xcdC C\xcdB\x8dB\x8dB\xcdC '
b'C\xcdB\x8dB\x8dB\xcdC \x7f\x01 \x7f\x01 '
b'C\x8dB\x8dB\x80\xf6\x8dB\x8dC C\xc0\x81\xcd'
b'B\xcdB\x8dB\x8dC C\xcdB\xcdB\x8dB\x8d'
b'C C\xcdB\xcdB\x8dB\x8dC C\xcdB\xcd'
b'B\x8dB\x8dC C\xcdB\xcdB\x8dB\x8dC '
b'C\xcdB\xcdB\x8dB\x8dC C\xcdB\xcdB\x8d'
b'B\x8dC C\xcdB\xcdB\x8dB\x8dC C\xcd'
b'B\xcdB\x8dB\x8dC C\xcdB\xcdB\x8dB\x8d'
b'C C\xcdB\xcdB\x8dB\x8dC C\xcdB\xcd'
b'B\x8dB\x8dC \x7f\x01 \x7f\x01 \x7f\x01\x10'
)
class Play2048App():
"""Let's play the 2048 game."""
NAME = '2048'
ICON = icon
def __init__(self):
"""Initialize the application."""
self._board = None
self._state = 0
self._confirmation_view = None
def foreground(self):
"""Activate the application."""
wasp.system.request_event(wasp.EventMask.TOUCH |
wasp.EventMask.SWIPE_UPDOWN |
wasp.EventMask.SWIPE_LEFTRIGHT)
self._state = 0
if not self._board:
self._start_game()
self._draw()
def touch(self,event):
"""Notify the application of a touchscreen touch event."""
if self._state == 0:
if not self._confirmation_view:
self._confirmation_view = widgets.ConfirmationView()
self._confirmation_view.draw('Restart game?')
self._state = 1
elif self._state == 1:
if self._confirmation_view.touch(event):
if self._confirmation_view.value:
self._start_game()
self._draw()
self._state = 0
def swipe(self, event):
"""Notify the application of a touchscreen swipe event."""
moved = False
if self._state == 0:
if event[0] == wasp.EventType.UP:
moved = self._shift(1,False)
elif event[0] == wasp.EventType.DOWN:
moved = self._shift(-1,False)
elif event[0] == wasp.EventType.LEFT:
moved = self._shift(1,True)
elif event[0] == wasp.EventType.RIGHT:
moved = self._shift(-1,True)
if moved:
self._add_tile()
def _draw(self):
"""Draw the display from scratch."""
board = self._board
draw = wasp.watch.drawable
draw.fill(GRID_BACKGROUND)
draw.set_font(fonts.sans24)
for y in range(GRID_SIZE):
for x in range(GRID_SIZE):
self._update(draw, board[y][x], y, x)
def _update(self, draw, cell, row, col):
"""Update the specified cell of the application display."""
x = GRID_PADDING + (col * (CELL_SIZE + GRID_PADDING))
y = GRID_PADDING + (row * (CELL_SIZE + GRID_PADDING))
draw.set_color(CELL_FOREGROUND[cell], CELL_BACKGROUND[cell])
draw.fill(CELL_BACKGROUND[cell], x, y, CELL_SIZE, CELL_SIZE)
draw.string(CELL_LABEL[cell], x, y + 16, CELL_SIZE)
def _start_game(self):
"""Start a new game."""
self._board = self._create_board()
self._add_tile()
self._add_tile()
def _create_board(self):
"""Create an empty 4x4 board."""
board = []
for _ in range(GRID_SIZE):
board.append([0] * GRID_SIZE)
return board
def _add_tile(self):
"""Add a new tile to a random empty location on the board."""
board = self._board
randint = random.randint
y = randint(0, GRID_SIZE-1)
x = randint(0, GRID_SIZE-1)
while board[y][x] != 0:
y = randint(0, GRID_SIZE-1)
x = randint(0, GRID_SIZE-1)
board[y][x] = 1
self._update(wasp.watch.drawable,1,y,x)
def _shift(self, direction, orientation):
"""Shift and merge the tiles vertically."""
draw = wasp.watch.drawable
update = self._update
board = self._board
moved = False
def read(y, x):
if not orientation:
y,x = x,y
return board[y][x]
def write(y, x, v):
if not orientation:
y,x = x,y
board[y][x] = v
update(draw, v, y, x)
if direction > 0:
s = 0 + 1
e = GRID_SIZE
else:
s = GRID_SIZE - 1 - 1
e = 0 - 1
for y in range(GRID_SIZE):
p = s - direction
for x in range(s,e,direction):
a = read(y,x)
b = read(y,p)
if a != 0:
if a == b:
write(y, p, a + 1)
write(y, x, 0)
moved = True
p += direction
else:
if b != 0:
p += direction
if x != p:
write(y, p, a)
write(y, x, 0)
moved = True
return moved

View File

@ -1,142 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020-21 Daniel Thompson
"""Settings application
~~~~~~~~~~~~~~~~~~~~~~~
Allows a very small set of user preferences (including the date and
time) to be set on the device itself.
.. figure:: res/SettingsApp.png
:width: 179
.. note::
The settings tool is not expected to comprehensively present every
user configurable preference. Some are better presented via a
companion app and some particular exotic ones are perhaps best
managed with a user-provided ``main.py``.
"""
import wasp
import fonts
import icons
class SettingsApp():
"""Settings application."""
NAME = 'Settings'
ICON = icons.settings
def __init__(self):
self._slider = wasp.widgets.Slider(3, 10, 90)
self._nfy_slider = wasp.widgets.Slider(3, 10, 90)
self._scroll_indicator = wasp.widgets.ScrollIndicator()
self._HH = wasp.widgets.Spinner(50, 60, 0, 23, 2)
self._MM = wasp.widgets.Spinner(130, 60, 0, 59, 2)
self._dd = wasp.widgets.Spinner(20, 60, 1, 31, 1)
self._mm = wasp.widgets.Spinner(90, 60, 1, 12, 1)
self._yy = wasp.widgets.Spinner(160, 60, 20, 60, 2)
self._settings = ['Brightness', 'Notification Level', 'Time', 'Date']
self._sett_index = 0
self._current_setting = self._settings[0]
def foreground(self):
self._slider.value = wasp.system.brightness - 1
self._draw()
wasp.system.request_event(wasp.EventMask.TOUCH)
wasp.system.request_event(wasp.EventMask.SWIPE_UPDOWN)
def touch(self, event):
if self._current_setting == 'Brightness':
self._slider.touch(event)
wasp.system.brightness = self._slider.value + 1
elif self._current_setting == 'Notification Level':
self._nfy_slider.touch(event)
wasp.system.notify_level = self._nfy_slider.value + 1
elif self._current_setting == 'Time':
if self._HH.touch(event) or self._MM.touch(event):
now = list(wasp.watch.rtc.get_localtime())
now[3] = self._HH.value
now[4] = self._MM.value
wasp.watch.rtc.set_localtime(now)
elif self._current_setting == 'Date':
if self._yy.touch(event) or self._mm.touch(event) \
or self._dd.touch(event):
now = list(wasp.watch.rtc.get_localtime())
now[0] = self._yy.value + 2000
now[1] = self._mm.value
now[2] = self._dd.value
wasp.watch.rtc.set_localtime(now)
self._update()
def swipe(self, event):
"""Handle NEXT events by augmenting the default processing by resetting
the count if we are not currently timing something.
No other swipe event is possible for this application.
"""
if event[0] == wasp.EventType.UP:
self._sett_index += 1
self._draw()
elif event[0] == wasp.EventType.DOWN:
self._sett_index -= 1
self._draw()
def _draw(self):
"""Redraw the display from scratch."""
draw = wasp.watch.drawable
mute = wasp.watch.display.mute
self._current_setting = self._settings[self._sett_index % len(self._settings)]
mute(True)
draw.fill()
draw.set_color(wasp.system.theme('bright'))
draw.set_font(fonts.sans24)
draw.string(self._current_setting, 0, 6, width=240)
if self._current_setting == 'Brightness':
self._slider.value = wasp.system.brightness - 1
elif self._current_setting == 'Notification Level':
self._nfy_slider.value = wasp.system.notify_level - 1
elif self._current_setting == 'Time':
now = wasp.watch.rtc.get_localtime()
self._HH.value = now[3]
self._MM.value = now[4]
draw.set_font(fonts.sans28)
draw.string(':', 110, 120-14, width=20)
self._HH.draw()
self._MM.draw()
elif self._current_setting == 'Date':
now = wasp.watch.rtc.get_localtime()
self._yy.value = now[0] - 2000
self._mm.value = now[1]
self._dd.value = now[2]
self._yy.draw()
self._mm.draw()
self._dd.draw()
draw.set_font(fonts.sans24)
draw.string('DD MM YY',0,180, width=240)
self._scroll_indicator.draw()
self._update()
mute(False)
def _update(self):
draw = wasp.watch.drawable
draw.set_color(wasp.system.theme('bright'))
if self._current_setting == 'Brightness':
if wasp.system.brightness == 3:
say = "High"
elif wasp.system.brightness == 2:
say = "Mid"
else:
say = "Low"
self._slider.update()
draw.string(say, 0, 150, width=240)
elif self._current_setting == 'Notification Level':
if wasp.system.notify_level == 3:
say = "High"
elif wasp.system.notify_level == 2:
say = "Mid"
else:
say = "Silent"
self._nfy_slider.update()
draw.string(say, 0, 150, width=240)

View File

@ -1,175 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Johannes Wache
"""Snake Game
~~~~~~~~~~~~~
This is a classic arcade game called snake.
.. figure:: res/SnakeApp.png
:width: 179
Screenshot of the snake game
You have to direct the white snake to the food block (blue dot) by swiping in the desired direction. You must not hit the border or the snake's body itself.
Every time the snake eats the food, its length increases by 1. (In the current version there is an error that the length of the snake is not increased by 1 when the snake gets the food for the first time. This has to be fixed).
Once the game is over, you can try again by tapping on the screen and then swipe in the direction you want to move. If you want to leave the game, simply wipe in any direction once the game is over.
And now: Have fun playing! :)
"""
# 2-bit RLE, generated from res/snake_icon.png, 280 bytes
snake_icon = (
b'\x02'
b'`@'
b'4@\x81M?\x14M?\x14M?\x14M?\x14M'
b'?\x14M?\x14M?\x14M?\x14M?U\x80\x0b'
b'\x8f?\x12\x8f\x10\xcd\x02\xcd%\x8f\x10\xcd\x02\xcd%\x8f'
b'\x10\xcd\x02\xcd%\x8f\x10\xcd\x02\xcd%\x8f\x10\xcd\x02\xcd'
b'%\x8f\x10\xcd\x02\xcd%\x8f\x10\xcd\x02\xcd%\x8f\x10\xcd'
b'\x02\xcd%\x8f\x10\xcd\x02\xcd%\x8f\x10\xcd\x02\xcd%\x8f'
b'\x10\xcd\x02\xcd%\x8f\x10\xcd\x02\xcd%\x8f\x10\xcd\x02\xcd'
b'%\x8f?s\xcd \xcd&\xcd \xcd&\xcd \xcd&'
b'\xcd \xcd&\xcd \xcd&\xcd \xcd&\xcd \xcd&'
b'\xcd \xcd&\xcd \xcd&\xcd \xcd&\xcd \xcd&'
b'\xcd \xcd&\xcd \xcd?\xa7\xcd\x02\xcd\x02\xcd\x02\xcd'
b'&\xcd\x02\xcd\x02\xcd\x02\xcd&\xcd\x02\xcd\x02\xcd\x02\xcd'
b'&\xcd\x02\xcd\x02\xcd\x02\xcd&\xcd\x02\xcd\x02\xcd\x02\xcd'
b'&\xcd\x02\xcd\x02\xcd\x02\xcd&\xcd\x02\xcd\x02\xcd\x02\xcd'
b'&\xcd\x02\xcd\x02\xcd\x02\xcd&\xcd\x02\xcd\x02\xcd\x02\xcd'
b'&\xcd\x02\xcd\x02\xcd\x02\xcd&\xcd\x02\xcd\x02\xcd\x02\xcd'
b'&\xcd\x02\xcd\x02\xcd\x02\xcd&\xcd\x02\xcd\x02\xcd\x02\xcd'
b'?\xff\xff\xff\x94'
)
import wasp, time
from random import randint
class SnakeGameApp():
NAME = 'Snake'
ICON = snake_icon
def __init__(self):
self.running = True
self.snake = Snake()
self.food_location()
self.highscore = 1
def foreground(self):
"""Activate the application."""
wasp.watch.drawable.fill()
if self.running:
self.update()
else:
self.snake.show()
wasp.watch.drawable.fill(x=self.food[0],y=self.food[1],w=15,h=15,bg=0x00ff)
wasp.system.request_event(wasp.EventMask.TOUCH |
wasp.EventMask.SWIPE_UPDOWN |
wasp.EventMask.SWIPE_LEFTRIGHT)
wasp.system.request_tick(250)
def touch(self,event):
if not self.running:
self.running = True
wasp.watch.drawable.fill()
def swipe(self, event):
if self.running:
"""Notify the application of a touchscreen swipe event."""
if event[0] == wasp.EventType.UP:
self.snake.set_dir(0,-15)
elif event[0] == wasp.EventType.DOWN:
self.snake.set_dir(0,15)
elif event[0] == wasp.EventType.LEFT:
self.snake.set_dir(-15,0)
elif event[0] == wasp.EventType.RIGHT:
self.snake.set_dir(15,0)
else:
return True
def tick(self, ticks):
"""Notify the application that its periodic tick is due."""
self.update()
def food_location(self):
x = randint(0,15) * 15
y = randint(0,15) * 15
self.food = [x,y]
def update(self):
draw = wasp.watch.drawable
"""Draw the display from scratch."""
if (self.snake.eat(self.food)):
self.food_location()
self.snake.update()
if (self.snake.end_game()):
if len(self.snake.body) > self.highscore:
self.highscore = len(self.snake.body)
self.running = False
wasp.watch.vibrator.pulse()
self.snake = Snake()
draw.fill()
draw.set_color(0xf000)
draw.string('GAME', 0, 60, width=240)
draw.string('OVER', 0, 98, width=240)
draw.string('Highscore: '+str(self.highscore-1),0,180,width=240)
draw.reset()
return True
if self.running:
self.snake.show()
draw.fill(x=self.food[0],y=self.food[1],w=15,h=15,bg=0x00ff)
return True
# Based on https://www.youtube.com/watch?v=OMoVcohRgZA
class Snake():
def __init__(self):
self.body = [[120,120]]
self.xdir = 0
self.ydir = 0
self.justate = False
self.oldtail = [0,0]
def set_dir(self,x,y):
self.xdir = x
self.ydir = y
def update(self):
self.oldtail = self.body[0].copy()
head = self.body[-1].copy()
if not self.justate:
self.body = self.body[1:]
self.justate = False
head[0] += self.xdir
head[1] += self.ydir
self.body.append(head)
def eat(self,pos):
x = self.body[-1][0]
y = self.body[-1][1]
if (x == pos[0] and y == pos[1]):
self.justate = True
# Color food white so it appears as a body part:
wasp.watch.drawable.fill(x=(self.body[-1][0]),y=(self.body[-1][1]),w=15,h=15,bg=0x0000)
wasp.watch.drawable.fill(x=self.body[-1][0]+1,y=self.body[-1][1]+1,w=13,h=13,bg=0xffff)
return True
return False
def end_game(self):
x = self.body[-1][0]
y = self.body[-1][1]
if (x >= 240 or x < 0) or (y >= 240 or y < 0):
return True
for i in range(len(self.body)-1):
part = self.body[i]
if (part[0] == x and part[1] == y):
return True
return False
def show(self):
draw = wasp.watch.drawable
draw.fill(x=self.oldtail[0],y=self.oldtail[1],w=15,h=15,bg=0x0000)
draw.fill(x=self.body[-1][0]+1,y=self.body[-1][1]+1,w=13,h=13,bg=0xffff)

View File

@ -1,114 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
"""Software
~~~~~~~~~~~
A tool to enable/disable applications.
.. figure:: res/SoftwareApp.png
:width: 179
Most applications are disabled by default at boot in order to conserve
RAM (which is in short supply and very useful to anyone wanting to
write an application). This tools allows us to boot and conserve RAM
whilst still allowing users to activate so many awesome applications!
"""
import wasp
import icons
class SoftwareApp():
"""Enable and disable applications."""
NAME = 'Software'
ICON = icons.software
def foreground(self):
"""Activate the application."""
def factory(label):
nonlocal y
cb = wasp.widgets.Checkbox(0, y, label)
y += 40
if y > 160:
y = 0
return cb
y = 0
db = []
db.append(('alarm', factory('Alarm')))
db.append(('calc', factory('Calculator')))
db.append(('chrono', factory('Chrono')))
db.append(('dual_clock', factory('Dual Clock')))
db.append(('fibonacci_clock', factory('Fibonacci Clock')))
db.append(('gameoflife', factory('Game Of Life')))
db.append(('musicplayer', factory('Music Player')))
db.append(('play2048', factory('Play 2048')))
db.append(('snake', factory('Snake Game')))
db.append(('flashlight', factory('Torch')))
db.append(('testapp', factory('Test')))
db.append(('timer', factory('Timer')))
db.append(('weather', factory('Weather')))
db.append(('word_clock', factory('Word Clock')))
# Get the initial state for the checkboxes
for _, checkbox in db:
label = checkbox.label.replace(' ', '')
for app in wasp.system.launcher_ring:
if type(app).__name__.startswith(label):
checkbox.state = True
break
self.si = wasp.widgets.ScrollIndicator()
self.page = 0
self.db = db
self._draw()
wasp.system.request_event(wasp.EventMask.TOUCH |
wasp.EventMask.SWIPE_UPDOWN)
def background(self):
del self.si
del self.page
del self.db
def get_page(self):
i = self.page * 5
return self.db[i:i+5]
def swipe(self, event):
"""Notify the application of a touchscreen swipe event."""
page = self.page
pages = (len(self.db)-1) // 5
if event[0] == wasp.EventType.DOWN:
page = page - 1 if page > 0 else pages
if event[0] == wasp.EventType.UP:
page = page + 1 if page < pages else 0
self.page = page
mute = wasp.watch.display.mute
mute(True)
self._draw()
mute(False)
def touch(self, event):
"""Notify the application of a touchscreen touch event."""
for module, checkbox in self.get_page():
if checkbox.touch(event):
label = checkbox.label.replace(' ', '')
if checkbox.state:
wasp.system.register('apps.{}.{}App'.format(module, label))
else:
for app in wasp.system.launcher_ring:
if type(app).__name__.startswith(label):
wasp.system.launcher_ring.remove(app)
break
break
def _draw(self):
"""Draw the display from scratch."""
wasp.watch.drawable.fill()
self.si.draw()
for _, checkbox in self.get_page():
checkbox.draw()

View File

@ -1,177 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
"""Step counter
~~~~~~~~~~~~~~~
Provide a daily step count.
.. figure:: res/StepsApp.png
:width: 179
The step counts automatically reset at midnight.
"""
import wasp
import fonts
import icons
import time
import watch
# 2-bit RLE, generated from res/feet.png, 240 bytes
feet = (
b'\x02'
b'00'
b'\x13\xc1-\xc4+\xc6*\xc6*\xc6&\xc3\x01\xc6\t\xc2'
b'\x1b\xc3\x02\xc5\x08\xc4\x1a\xc4\x01\xc5\x08\xc5\x19\xc4\x02\xc3'
b'\x08\xc6\x17\xc1\x02\xc3\x02\xc3\x08\xc6\x16\xc3\x02\xc1\x0e\xc6'
b'\x01\xc3\x12\xc3\x11\xc6\x01\xc3\x13\xc2\x05\xc2\n\xc5\x02\xc3'
b'\x10\xc2\x01\xc2\x02\xc6\n\xc4\x01\xc4\x10\xc2\x04\xc7\x0b\xc1'
b'\x03\xc3\x11\xc3\x02\xc8\x10\xc2\x01\xc3\r\xc2\x02\xc9\x13\xc3'
b'\x0b\xc1\x05\xc9\x0c\xc2\x05\xc3\x0b\xc2\x03\xc9\x0c\xc5\x03\xc2'
b'\x0c\xc2\x02\xca\x0c\xc6\x05\xc2\t\xc2\x02\xca\x0c\xc7\x03\xc3'
b'\r\xca\x0c\xc8\x02\xc3\x0c\xca\r\xc9\x02\xc1\r\xca\r\xc9'
b'\x04\xc2\n\xca\x0e\xc9\x02\xc3\n\xca\x0e\xc9\x02\xc2\x0b\xca'
b'\x0e\xca\x0e\xca\x0e\xca\x0f\xc9\x0e\xca\x0f\xca\r\xca\x0f\xca'
b'\r\xca\x10\xcb\x0b\xca\x10\xcc\n\xca\x10\xcd\t\xca\x11\xcc'
b'\x08\xca\x12\xcc\x07\xcb\x13\xcb\x06\xcb\x14\xcb\x05\xcc\x15\xca'
b'\x04\xcc\x16\xc9\x05\xcc\x17\xc7\x05\xcd\x17\xc7\x05\xcc\x1a\xc4'
b"\x07\xcb%\xca&\xca'\xc8)\xc6+\xc4\x0e"
)
class StepCounterApp():
"""Step counter application."""
NAME = 'Steps'
ICON = icons.app
def __init__(self):
watch.accel.reset()
self._scroll = wasp.widgets.ScrollIndicator()
self._wake = 0
def foreground(self):
"""Cancel the alarm and draw the application.
Cancelling the alarm has two effects. Firstly it ensures the
step count won't change whilst we are watching it and, secondly
it ensures that if the time of day has been set to a value in
the past that we reconfigure the alarm.
This does in the side effect that if the application of open at
midnight then the reset doesn't happen for that day.
"""
wasp.system.cancel_alarm(self._wake, self._reset)
wasp.system.bar.clock = True
self._page = -1
self._draw()
wasp.system.request_event(wasp.EventMask.SWIPE_UPDOWN)
wasp.system.request_tick(1000)
def background(self):
"""Set an alarm to trigger at midnight and reset the counter."""
now = watch.rtc.get_localtime()
yyyy = now[0]
mm = now[1]
dd = now[2]
then = (yyyy, mm, dd+1, 0, 0, 0, 0, 0, 0)
self._wake = time.mktime(then)
wasp.system.set_alarm(self._wake, self._reset)
def _reset(self):
""""Reset the step counter and re-arm the alarm."""
watch.accel.steps = 0
self._wake += 24 * 60 * 60
wasp.system.set_alarm(self._wake, self._reset)
def swipe(self, event):
if event[0] == wasp.EventType.DOWN:
if self._page == -1:
return
self._page -= 1
else:
self._page += 1
mute = wasp.watch.display.mute
mute(True)
self._draw()
mute(False)
def tick(self, ticks):
if self._page == -1:
self._update()
def _draw(self):
"""Draw the display from scratch."""
draw = wasp.watch.drawable
draw.fill()
if self._page == -1:
self._update()
wasp.system.bar.draw()
else:
self._update_graph()
def _update(self):
draw = wasp.watch.drawable
# Draw the icon
draw.blit(feet, 12, 132-24)
# Update the status bar
now = wasp.system.bar.update()
# Update the scroll indicator
scroll = self._scroll
scroll.up = False
scroll.draw()
# Update the step count
count = watch.accel.steps
t = str(count)
w = fonts.width(fonts.sans36, t)
draw.set_font(fonts.sans36)
draw.set_color(draw.lighten(wasp.system.theme('spot1'), wasp.system.theme('contrast')))
draw.string(t, 228-w, 132-18)
def _update_graph(self):
draw = watch.drawable
draw.set_font(fonts.sans24)
draw.set_color(0xffff)
# Draw the date
now = int(watch.rtc.time())
then = now - ((24*60*60) * self._page)
walltime = time.localtime(then)
draw.string('{:02d}-{:02d}'.format(walltime[2], walltime[1]), 0, 0)
# Get the iterable step date for the currently selected date
data = wasp.system.steps.data(then)
# Bail if there is no data
if not data:
draw.string('No data', 239-160, 0, 160, right=True)
return
color = wasp.system.theme('spot2')
# Draw the frame
draw.fill(0x3969, 0, 39, 240, 1)
draw.fill(0x3969, 0, 239, 240, 1)
for i in (0, 60, 90, 120, 150, 180, 239):
draw.fill(0x3969, i, 39, 1, 201)
total = 0
for x, d in enumerate(data):
if d == 0 or x < 2:
# TODO: the x < 2 conceals BUGZ
continue
total += d
d = d // 4
if d > 200:
draw.fill(0xffff, x, 239-200, 1, 200)
else:
draw.fill(color, x, 239-d, 1, d)
draw.string(str(total), 239-160, 0, 160, right=True)

View File

@ -1,98 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
"""The complete set of wasp-os application entry points are documented
below as part of a template application. Note that the template does
not rely on any specific parent class. This is because applications in
wasp-os can rely on *duck typing* making a class hierarchy pointless.
"""
import wasp
import icons
class TemplateApp():
"""Template application.
The template application includes every application entry point. It
is used as a reference guide and can also be used as a template for
creating new applications.
.. data:: NAME = 'Template'
Applications must provide a short ``NAME`` that is used by the
launcher to describe the application. Names that are longer than
8 characters are likely to be abridged by the launcher in order
to fit on the screen.
.. data:: ICON = RLE2DATA
Applications can optionally provide an icon for display by the
launcher. Applications that expect to be installed on the quick
ring will not be listed by the launcher and need not provide any
icon. When no icon is provided the system will use a default
icon.
The icon is an opportunity to differentiate your application from others
so supplying an icon is strongly recommended. The icon, when provided,
must not be larger than 96x64.
"""
NAME = 'Template'
ICON = icons.app
def __init__(self):
"""Initialize the application."""
pass
def foreground(self):
"""Activate the application."""
self._draw()
wasp.system.request_event(wasp.EventMask.TOUCH |
wasp.EventMask.SWIPE_UPDOWN |
wasp.EventMask.BUTTON)
wasp.system.request_tick(1000)
def background(self):
"""De-activate the application."""
pass
def sleep(self):
"""Notify the application the device is about to sleep."""
return False
def wake(self):
"""Notify the application the device is waking up."""
pass
def press(self, button, state):
"""Notify the application of a button-press event."""
draw = wasp.watch.drawable
draw.string('Button', 0, 108, width=240)
def swipe(self, event):
"""Notify the application of a touchscreen swipe event."""
draw = wasp.watch.drawable
if event[0] == wasp.EventType.UP:
draw.string('Swipe up', 0, 108, width=240)
else:
draw.string('Swipe down', 0, 108, width=240)
def tick(self, ticks):
"""Notify the application that its periodic tick is due."""
pass
def touch(self, event):
"""Notify the application of a touchscreen touch event."""
draw = wasp.watch.drawable
wasp.watch.drawable.string('({}, {})'.format(
event[1], event[2]), 0, 108, width=240)
def _draw(self):
"""Draw the display from scratch."""
draw = wasp.watch.drawable
draw.fill()
draw.string(self.NAME, 0, 6, width=240)
self._update()
def _update(self):
"""Update the dynamic parts of the application display."""
pass

View File

@ -1,267 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
"""Self Tests
~~~~~~~~~~~~~
A collection of tests used to develop features or provide useful metrics such
as performance indicators or memory usage.
.. figure:: res/SelfTestApp.png
:width: 179
"""
import wasp
import gc
import fonts
import icons
import machine
from apps.pager import PagerApp
class TestApp():
"""Self test application."""
NAME = 'Self Test'
ICON = icons.app
def __init__(self):
self.tests = ('Alarm', 'Button', 'Checkbox', 'Crash', 'Colours', 'Fill', 'Fill-H', 'Fill-V', 'Free Mem', 'Line', 'Notifications', 'RLE', 'String', 'Touch', 'Wrap')
self.test = self.tests[0]
self.scroll = wasp.widgets.ScrollIndicator()
self._checkbox = wasp.widgets.Checkbox(4, 104, 'Check me')
self._sliders = (
wasp.widgets.Slider(32, 10, 90, 0xf800),
wasp.widgets.Slider(64, 10, 140, 0x27e4),
wasp.widgets.Slider(32, 10, 190, 0x211f),
)
self._spinner = wasp.widgets.Spinner(90, 60, 0, 99)
def foreground(self):
"""Activate the application."""
self.on_screen = ( -1, -1, -1, -1, -1, -1 )
self._draw()
wasp.system.request_event(wasp.EventMask.TOUCH |
wasp.EventMask.SWIPE_UPDOWN |
wasp.EventMask.BUTTON)
def press(self, button, state):
draw = wasp.watch.drawable
if self.test == 'Alarm':
self._test_alarm()
elif self.test == 'Button':
draw.string('{}: {}'.format(button, state), 0, 108, width=240)
elif self.test == 'Crash':
self.crash()
elif self.test == 'String':
self._benchmark_string()
elif self.test == 'Touch':
draw.string('Button', 0, 108, width=240)
def swipe(self, event):
tests = self.tests
i = tests.index(self.test)
if event[0] == wasp.EventType.UP:
i += 1
if i >= len(tests):
i = 0
else:
i -= 1
if i < 0:
i = len(tests) - 1
self.test = tests[i]
self._draw()
def touch(self, event):
if self.test == 'Checkbox':
self._checkbox.touch(event)
elif self.test == 'Colours':
if event[2] > 90:
s = self._sliders[(event[2] - 90) // 50]
s.touch(event)
s.update()
self.scroll.draw()
self._update_colours()
elif self.test.startswith('Fill'):
self._benchmark_fill()
elif self.test == 'Notifications':
if self._spinner.touch(event):
notifications = wasp.system.notifications
if len(notifications) > self._spinner.value:
wasp.system.unnotify(
next(iter(notifications.keys())))
else:
wasp.system.notify(wasp.watch.rtc.get_uptime_ms(),
{
"src":"Hangouts",
"title":"A Name",
"body":"message contents"
})
elif self.test == 'RLE':
self._benchmark_rle()
elif self.test == 'String':
self._benchmark_string()
elif self.test == 'Touch':
wasp.watch.drawable.string('({}, {})'.format(
event[1], event[2]), 0, 108, width=240)
elif self.test == 'Wrap':
self._benchmark_wrap()
elif self.test == 'Line':
self._benchmark_line()
def _alarm(self):
wasp.system.wake()
wasp.system.switch(PagerApp('Alarm triggered'))
def _test_alarm(self):
def nop():
pass
now = wasp.watch.rtc.time()
wasp.system.set_alarm(now + 30, self._alarm)
wasp.system.set_alarm(now + 30, nop)
if not wasp.system.cancel_alarm(now + 30, nop):
bug()
wasp.watch.drawable.string("Done.", 12, 24+80)
def _benchmark_rle(self):
draw = wasp.watch.drawable
draw.fill(0, 0, 30, 240, 240-30)
self.scroll.draw()
t = machine.Timer(id=1, period=8000000)
t.start()
for i in range(0, 128, 16):
draw.blit(icons.software, i+16, i+32)
elapsed = t.time()
t.stop()
del t
draw.string('{}s'.format(elapsed / 1000000), 12, 24+192)
def _benchmark_fill(self):
draw = wasp.watch.drawable
draw.fill(0, 0, 30, 240, 240-30)
self.scroll.draw()
t = machine.Timer(id=1, period=8000000)
if self.test == 'Fill':
t.start()
draw.fill(0xffff, 60, 60, 120, 120)
elapsed = t.time()
elif self.test == 'Fill-H':
t.start()
for i in range(60, 180, 2):
draw.fill(0xffff, 60, i, 120, 1)
elapsed = t.time()
elif self.test == 'Fill-V':
t.start()
for i in range(60, 180, 2):
draw.fill(0xffff, i, 60, 1, 120)
elapsed = t.time()
t.stop()
del t
draw.string('{}s'.format(elapsed / 1000000), 12, 24+192)
def _benchmark_string(self):
draw = wasp.watch.drawable
draw.fill(0, 0, 30, 240, 240-30)
draw.set_color(0xffff, 0x4208)
self.scroll.draw()
t = machine.Timer(id=1, period=8000000)
t.start()
draw.string("The quick brown", 12, 24+24)
draw.string("fox jumped over", 12, 24+48)
draw.string("the lazy dog.", 12, 24+72)
draw.string("0123456789", 12, 24+120, width=228)
draw.string('!"£$%^&*()', 12, 24+144, width=228)
elapsed = t.time()
t.stop()
del t
draw.string('{}s'.format(elapsed / 1000000), 12, 24+192)
def _benchmark_line(self):
draw = wasp.watch.drawable
# instead of calculating by trig functions, use LUT
points = (0, 50), (19, 46), (35, 35), (46, 19),
draw.fill(0, 70, 70, 100, 100)
self.scroll.draw()
t = machine.Timer(id=1, period=8000000)
t.start()
for x, y in points:
draw.line(120, 120, 120+x, 120+y, 4, 0xfb00) # red
draw.line(120, 120, 120+y, 120-x, 3, 0x07c0) # green
draw.line(120, 120, 120-x, 120-y, 5, 0x6b3f) # blue
draw.line(120, 120, 120-y, 120+x, 2, 0xffe0) # yellow
elapsed = t.time()
t.stop()
del t
draw.string('{}s'.format(elapsed / 1000000), 12, 24+192)
def _benchmark_wrap(self):
draw = wasp.watch.drawable
draw.fill(0, 0, 30, 240, 240-30)
self.scroll.draw()
t = machine.Timer(id=1, period=8000000)
t.start()
draw = wasp.watch.drawable
s = 'This\nis a very long string that will need to be wrappedinmultipledifferentways!'
chunks = draw.wrap(s, 240)
for i in range(len(chunks)-1):
sub = s[chunks[i]:chunks[i+1]].rstrip()
draw.string(sub, 0, 48+24*i)
elapsed = t.time()
t.stop()
del t
draw.string('{}s'.format(elapsed / 1000000), 12, 24+192)
def _draw(self):
"""Redraw the display from scratch."""
wasp.watch.display.mute(True)
draw = wasp.watch.drawable
draw.fill()
draw.set_font(fonts.sans24)
draw.string('{} test'.format(self.test),
0, 6, width=240)
if self.test == 'Alarm':
draw.string("Press button to", 12, 24+24)
draw.string("set alarm.", 12, 24+48)
elif self.test == 'Checkbox':
self._checkbox.draw()
elif self.test == 'Crash':
draw.string("Press button to", 12, 24+24)
draw.string("throw exception.", 12, 24+48)
elif self.test == 'Colours':
for s in self._sliders:
s.draw()
self._update_colours()
elif self.test == 'Free Mem':
if wasp.watch.free:
draw.string("Boot: {}".format(wasp.watch.free), 12, 3*24)
draw.string("Init: {}".format(wasp.free), 12, 4*24)
draw.string("Now: {}".format(gc.mem_free()), 12, 5*24)
gc.collect()
draw.string("GC: {}".format(gc.mem_free()), 12, 6*24)
else:
draw.string("Not supported", 12, 4*24)
elif self.test == 'Notifications':
self._spinner.value = len(wasp.system.notifications)
self._spinner.draw()
elif self.test == 'RLE':
draw.blit(self.ICON, 120-48, 120-32)
self.scroll.draw()
wasp.watch.display.mute(False)
def _update_colours(self):
draw = wasp.watch.drawable
r = self._sliders[0].value
g = self._sliders[1].value
b = self._sliders[2].value
rgb = (r << 11) + (g << 5) + b
draw.string('RGB565 #{:04x}'.format(rgb), 0, 6, width=240)
draw.fill(rgb, 60, 35, 120, 50)

View File

@ -1,164 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
# Copyright (C) 2020 Carlos Gil
"""Weather for GadgetBridge and wasp-os companion
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. figure:: res/WeatherApp.png
:width: 179
Screenshot of the Weather application
"""
import wasp
import icons
import time
import fonts.sans36
# 2-bit RLE, generated from res/weather_icon.png, 309 bytes
icon = (
b'\x02'
b'`@'
b'\x1e\xa4<\xa4<\xa4;\xa5?Y\xec2\xf0/\xf2-'
b'\xf4,\xc3@;n\xc3,\xc3n\xc3,\xc3n\xc3,'
b'\xc3n\xc3,\xc3n\xc3,\xc3n\xc3,\xc3n\xc3,'
b'\xc3n\xc3,\xc3n\xc3,\xc3O\x80\xd3\x87X\xc3,'
b'\xc3M\x8bV\xc3,\xc3K\x8fT\xc3,\xc3J\x91S'
b'\xc3,\xc3I\x93R\xc3,\xc3H\x95Q\xc3,\xc3H'
b'\x95Q\xc3,\xc3G\x97P\xc3,\xc3G\x97P\xc3+'
b'\xc4F\x99O\xc3*\xc5F\x99O\xc3*\xc5F\x99O'
b'\xc3*\xc5F\x99O\xc3*\xc5F\x99O\xc3*\xc5F'
b'\x99\xc4K\xc3*\xc5F\x8e\xc5\x84\xc1\x81\xc5J\xc3*'
b'\xc5G\x8c\xc8\x81\xcaH\xc3+\xc4G\x83\xc5\x83\xd6F'
b'\xc3,\xc3H\x81\xe0E\xc3,\xc3G\xe2E\xc3,\xc3'
b'G\xe3D\xc3,\xc3F\xe4D\xc3,\xc3F\xe4D\xc3'
b',\xc3F\xe4D\xc3,\xc3F\xe4D\xc3,\xc3F\xe3'
b'E\xc3,\xc3G\xe2E\xc3,\xc3G\xe1F\xc3,\xc3'
b'H\xc9A\xccC\xc5H\xc3,\xc3J\xc5E\xc9Q\xc3'
b',\xc3V\xc5S\xc3,\xc3n\xc3,\xc3n\xc3,\xc3'
b'n\xc3,\xc3n\xc3,\xc3n\xc3,\xc3n\xc3,\xf4'
b'-\xf2/\xf02\xec?X\xc0\xdb\xe6;\xe4<\xe4<'
b'\xe4\x1e'
)
class WeatherApp(object):
""" Weather application."""
NAME = 'Weather'
ICON = icon
def __init__(self):
self._temp = ''
self._hum = ''
self._txt = ''
self._wind = ''
self._loc = ''
self._temp_changed = True
self._hum_changed = True
self._txt_changed = True
self._wind_changed = True
self._loc_changed = True
def foreground(self):
"""Activate the application."""
temp = wasp.system.weatherinfo.get('temp')
hum = wasp.system.weatherinfo.get('hum')
txt = wasp.system.weatherinfo.get('txt')
wind = wasp.system.weatherinfo.get('wind')
loc = wasp.system.weatherinfo.get('loc')
if temp:
self._temp = temp
if hum:
self._hum = hum
if txt:
self._txt = txt
if wind:
self._wind = wind
if loc:
self._loc = loc
wasp.watch.drawable.fill()
self.draw()
wasp.system.request_tick(1000)
def background(self):
"""De-activate the application (without losing state)."""
self._temp_changed = True
self._hum_changed = True
self._txt_changed = True
self._wind_changed = True
self._loc_changed = True
def tick(self, ticks):
wasp.system.keep_awake()
temp_now = wasp.system.weatherinfo.get('temp')
hum_now = wasp.system.weatherinfo.get('hum')
txt_now = wasp.system.weatherinfo.get('txt')
wind_now = wasp.system.weatherinfo.get('wind')
loc_now = wasp.system.weatherinfo.get('loc')
if temp_now:
if temp_now != self._temp:
self._temp = temp_now
self._temp_changed = True
else:
self._temp_changed = False
if hum_now:
if hum_now != self._hum:
self._hum = hum_now
self._hum_changed = True
else:
self._hum_changed = False
if txt_now:
if txt_now != self._txt:
self._txt = txt_now
self._txt_changed = True
else:
self._txt_changed = False
if wind_now:
if wind_now != self._wind:
self._wind = wind_now
self._wind_changed = True
else:
self._wind_changed = False
if loc_now:
if loc_now != self._loc:
self._loc = loc_now
self._loc_changed = True
else:
self._loc_changed = False
wasp.system.weatherinfo = {}
self._update()
def draw(self):
"""Redraw the display from scratch."""
self._draw()
def _draw(self):
"""Redraw the updated zones."""
if self._temp_changed:
self._draw_label(self._temp, 54, 36)
if self._hum_changed:
self._draw_label("Humidity: "+self._hum, 160)
if self._txt_changed:
self._draw_label(self._txt, 12)
if self._wind_changed:
self._draw_label("Wind: "+self._wind, 120)
if self._loc_changed:
self._draw_label(self._loc, 200)
def _draw_label(self, label, pos, size = 24):
"""Redraw label info"""
if label:
draw = wasp.watch.drawable
draw.reset()
if size == 36:
draw.set_font(fonts.sans36)
draw.string(label, 0, pos, 240)
def _update(self):
self._draw()
def update(self):
pass

View File

@ -1,195 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2020 Daniel Thompson
# Copyright (C) 2021 Brendan Sleight
"""Word clock
~~~~~~~~~~~~~~~~
Shows a time as words together with a battery meter and the date.
.. figure:: res/WordClkApp.png
:width: 179
"""
import wasp
import icons
MONTH = 'JanFebMarAprMayJunJulAugSepOctNovDec'
class WordClockApp():
"""Simple digital clock application."""
NAME = 'WordClk'
ICON = icons.app
def foreground(self):
"""Activate the application.
Configure the status bar, redraw the display and request a periodic
tick callback every second.
"""
wasp.system.bar.clock = False
self._draw(True)
wasp.system.request_tick(1000)
def sleep(self):
"""Prepare to enter the low power mode.
:returns: True, which tells the system manager not to automatically
switch to the default application before sleeping.
"""
return True
def wake(self):
"""Return from low power mode.
Time will have changes whilst we have been asleep so we must
udpate the display (but there is no need for a full redraw because
the display RAM is preserved during a sleep.
"""
self._draw()
def tick(self, ticks):
"""Periodic callback to update the display."""
self._draw()
def _draw(self, redraw=False):
"""Draw or lazily update the display.
The updates are as lazy by default and avoid spending time redrawing
if the time on display has not changed. However if redraw is set to
True then a full redraw is be performed.
"""
draw = wasp.watch.drawable
hi = wasp.system.theme('bright')
if redraw:
now = wasp.watch.rtc.get_localtime()
# Clear the display and draw that static parts of the watch face
draw.fill()
# Redraw the status bar
wasp.system.bar.draw()
else:
# The update is doubly lazy... we update the status bar and if
# the status bus update reports a change in the time of day
# then we compare the minute on display to make sure we
# only update the main clock once per minute.
now = wasp.system.bar.update()
if not now or self._min == now[4]:
# Skip the update
return
draw.set_color(hi)
# Format the month as text
month = now[1] - 1
month = MONTH[month*3:(month+1)*3]
# Record the minute that is currently being displayed
self._hour = now[3]
self._min = now[4]
# Testing
# self._hour = 23
# self._min = 59
# Convert to words
part_day = ""
hour = ""
part_hour = ""
minute_words= ""
part_day = ""
hours_a = ["midnight", "one", "two",
"three", "four", "five",
"six", "seven", "eight",
"nine", "ten", "eleven",
"twelve",
"one", "two",
"three", "four", "five",
"six", "seven", "eight",
"nine", "ten", "eleven"]
if (self._min > 32):
hour = hours_a[(self._hour + 1) % 24]
else:
hour = hours_a[self._hour % 24]
if (hour != "midnight" and hour != "twelve"):
if (self._hour >= 22):
part_day = " at night"
elif (self._hour >= 18):
part_day = " in the evening"
elif (self._hour >= 12):
part_day = " in the afternoon"
elif (self._hour >= 6):
part_day = " in the morning"
elif (self._hour >= 3):
part_day = " in the early hours"
elif (self._hour >= 0):
part_day = " at night"
if (self._min > 57):
part_hour = ""
elif (self._min > 52):
part_hour = "five to "
elif (self._min > 47):
part_hour = "ten to "
elif (self._min > 42):
part_hour = "quarter to "
elif (self._min > 37):
part_hour = "twenty to "
elif (self._min > 32):
part_hour = "twenty-five to "
elif (self._min > 27):
part_hour = "half past "
elif (self._min > 22):
part_hour = "twenty-five past "
elif (self._min > 17):
part_hour = "twenty past "
elif (self._min > 12):
part_hour = "quarter past "
elif (self._min > 7):
part_hour = "ten past "
elif (self._min > 2):
part_hour = "five past "
else:
part_hour = ""
minute_words_int = (self._min % 5)
if (minute_words_int == 4):
minute_words = "almost"
if (minute_words_int == 3):
minute_words = "coming up to"
if (minute_words_int == 2):
minute_words = "after"
if (minute_words_int == 1):
minute_words = "just gone"
self._words = ""
if (minute_words !=""):
self._words = minute_words + "\n"
if (part_hour !=""):
self._words = self._words + part_hour + "\n"
self._words = self._words + hour + "\n"
if (part_day !=""):
self._words = self._words + part_day
# No capitilise in Micropython
# ASCII convert
self._words = chr(ord(self._words[0])-32) + self._words[1:]
# Some phases may be 5 lines long, some may be 1
draw.fill(0, 0, 48)
chunks = draw.wrap(self._words, 240)
lines_of_text = len(chunks)-1
offset_y=int(((5-lines_of_text/2)*26))
for i in range(len(chunks)-1):
sub = self._words[chunks[i]:chunks[i+1]].rstrip()
draw.string(sub, 0, offset_y+26*i, 240)
draw.string('{} {} {}'.format(now[2], month, now[0]),
0, 214, width=240)