From df6bcefdebe83e62024ae19843961a36c5272166 Mon Sep 17 00:00:00 2001 From: jebbatime build server Date: Mon, 14 Jun 2021 11:22:11 -0600 Subject: [PATCH] rm unused apps --- wasp/apps/chrono.py | 101 ------------- wasp/apps/demo.py | 270 ----------------------------------- wasp/apps/dual_clock.py | 122 ---------------- wasp/apps/fibonacci_clock.py | 119 --------------- wasp/apps/gameoflife.py | 258 --------------------------------- wasp/apps/haiku.py | 65 --------- wasp/apps/heart.py | 127 ---------------- wasp/apps/hello.py | 19 --- wasp/apps/musicplayer.py | 211 --------------------------- wasp/apps/play2048.py | 217 ---------------------------- wasp/apps/settings.py | 142 ------------------ wasp/apps/snake.py | 175 ----------------------- wasp/apps/software.py | 114 --------------- wasp/apps/steps.py | 177 ----------------------- wasp/apps/template.py | 98 ------------- wasp/apps/testapp.py | 267 ---------------------------------- wasp/apps/weather.py | 164 --------------------- wasp/apps/word_clock.py | 195 ------------------------- 18 files changed, 2841 deletions(-) delete mode 100644 wasp/apps/chrono.py delete mode 100644 wasp/apps/demo.py delete mode 100644 wasp/apps/dual_clock.py delete mode 100644 wasp/apps/fibonacci_clock.py delete mode 100644 wasp/apps/gameoflife.py delete mode 100644 wasp/apps/haiku.py delete mode 100644 wasp/apps/heart.py delete mode 100644 wasp/apps/hello.py delete mode 100644 wasp/apps/musicplayer.py delete mode 100644 wasp/apps/play2048.py delete mode 100644 wasp/apps/settings.py delete mode 100644 wasp/apps/snake.py delete mode 100644 wasp/apps/software.py delete mode 100644 wasp/apps/steps.py delete mode 100644 wasp/apps/template.py delete mode 100644 wasp/apps/testapp.py delete mode 100644 wasp/apps/weather.py delete mode 100644 wasp/apps/word_clock.py diff --git a/wasp/apps/chrono.py b/wasp/apps/chrono.py deleted file mode 100644 index 3eae3a1..0000000 --- a/wasp/apps/chrono.py +++ /dev/null @@ -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) diff --git a/wasp/apps/demo.py b/wasp/apps/demo.py deleted file mode 100644 index 17b31c6..0000000 --- a/wasp/apps/demo.py +++ /dev/null @@ -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<"\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< = len(colors): - self._color = 0 diff --git a/wasp/apps/dual_clock.py b/wasp/apps/dual_clock.py deleted file mode 100644 index 39bb8e0..0000000 --- a/wasp/apps/dual_clock.py +++ /dev/null @@ -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] diff --git a/wasp/apps/fibonacci_clock.py b/wasp/apps/fibonacci_clock.py deleted file mode 100644 index 0f9ebfd..0000000 --- a/wasp/apps/fibonacci_clock.py +++ /dev/null @@ -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] diff --git a/wasp/apps/gameoflife.py b/wasp/apps/gameoflife.py deleted file mode 100644 index 7e2b518..0000000 --- a/wasp/apps/gameoflife.py +++ /dev/null @@ -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\x1dDB"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\x08DB\x02' - b'B\x06\x82\x06\x82\x06B\x02B=H\x04\x84\x04\x84\x04' - b'H\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) diff --git a/wasp/apps/haiku.py b/wasp/apps/haiku.py deleted file mode 100644 index 2e8c8a6..0000000 --- a/wasp/apps/haiku.py +++ /dev/null @@ -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() diff --git a/wasp/apps/heart.py b/wasp/apps/heart.py deleted file mode 100644 index 5d181b0..0000000 --- a/wasp/apps/heart.py +++ /dev/null @@ -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() diff --git a/wasp/apps/hello.py b/wasp/apps/hello.py deleted file mode 100644 index 586082a..0000000 --- a/wasp/apps/hello.py +++ /dev/null @@ -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) diff --git a/wasp/apps/musicplayer.py b/wasp/apps/musicplayer.py deleted file mode 100644 index 493176b..0000000 --- a/wasp/apps/musicplayer.py +++ /dev/null @@ -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 diff --git a/wasp/apps/play2048.py b/wasp/apps/play2048.py deleted file mode 100644 index 6db2685..0000000 --- a/wasp/apps/play2048.py +++ /dev/null @@ -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 diff --git a/wasp/apps/settings.py b/wasp/apps/settings.py deleted file mode 100644 index 6c5b664..0000000 --- a/wasp/apps/settings.py +++ /dev/null @@ -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) diff --git a/wasp/apps/snake.py b/wasp/apps/snake.py deleted file mode 100644 index fc00138..0000000 --- a/wasp/apps/snake.py +++ /dev/null @@ -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) diff --git a/wasp/apps/software.py b/wasp/apps/software.py deleted file mode 100644 index dcc412e..0000000 --- a/wasp/apps/software.py +++ /dev/null @@ -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() diff --git a/wasp/apps/steps.py b/wasp/apps/steps.py deleted file mode 100644 index 021db38..0000000 --- a/wasp/apps/steps.py +++ /dev/null @@ -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) diff --git a/wasp/apps/template.py b/wasp/apps/template.py deleted file mode 100644 index e611545..0000000 --- a/wasp/apps/template.py +++ /dev/null @@ -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 diff --git a/wasp/apps/testapp.py b/wasp/apps/testapp.py deleted file mode 100644 index f4a9a0e..0000000 --- a/wasp/apps/testapp.py +++ /dev/null @@ -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) diff --git a/wasp/apps/weather.py b/wasp/apps/weather.py deleted file mode 100644 index 2e44da4..0000000 --- a/wasp/apps/weather.py +++ /dev/null @@ -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 diff --git a/wasp/apps/word_clock.py b/wasp/apps/word_clock.py deleted file mode 100644 index a7f2d63..0000000 --- a/wasp/apps/word_clock.py +++ /dev/null @@ -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)