summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------micropython0
-rw-r--r--wasp/apps/clock.py2
-rw-r--r--wasp/apps/pager.py34
-rw-r--r--wasp/apps/testapp.py22
-rw-r--r--wasp/boards/pinetime/manifest.py1
-rw-r--r--wasp/gadgetbridge.py60
-rw-r--r--wasp/icons.py13
-rw-r--r--wasp/main.py1
-rw-r--r--wasp/wasp.py19
-rw-r--r--wasp/widgets.py31
10 files changed, 171 insertions, 12 deletions
diff --git a/micropython b/micropython
-Subproject df61f43d562ec33388bdb27b07fb6f0bbdbe8b8
+Subproject 13f086deeb8b9195886fbbda0751302a67ff3c1
diff --git a/wasp/apps/clock.py b/wasp/apps/clock.py
index f09f2d4..a3134a0 100644
--- a/wasp/apps/clock.py
+++ b/wasp/apps/clock.py
@@ -35,6 +35,7 @@ class ClockApp():
def __init__(self):
self.meter = wasp.widgets.BatteryMeter()
+ self.notifier = wasp.widgets.Notifier()
def foreground(self):
"""Activate the application."""
@@ -87,4 +88,5 @@ class ClockApp():
0, 180, width=240)
self.meter.update()
+ self.notifier.update()
return True
diff --git a/wasp/apps/pager.py b/wasp/apps/pager.py
index 18322d2..edb5418 100644
--- a/wasp/apps/pager.py
+++ b/wasp/apps/pager.py
@@ -26,16 +26,13 @@ class PagerApp():
def foreground(self):
"""Activate the application."""
- self._page = 0
- self._chunks = wasp.watch.drawable.wrap(self._msg, 240)
- self._numpages = (len(self._chunks) - 2) // 9
wasp.system.request_event(wasp.EventMask.SWIPE_UPDOWN)
- self._draw()
+ self._redraw()
def background(self):
"""De-activate the application."""
- del self._chunks
- del self._numpages
+ self._chunks = None
+ self._numpages = None
def swipe(self, event):
"""Swipe to page up/down."""
@@ -55,8 +52,15 @@ class PagerApp():
self._draw()
mute(False)
+ def _redraw(self):
+ """Redraw from scratch (jump to the first page)"""
+ self._page = 0
+ self._chunks = wasp.watch.drawable.wrap(self._msg, 240)
+ self._numpages = (len(self._chunks) - 2) // 9
+ self._draw()
+
def _draw(self):
- """Draw the display from scratch."""
+ """Draw a page from scratch."""
draw = wasp.watch.drawable
draw.fill()
@@ -73,6 +77,22 @@ class PagerApp():
scroll.down = page < self._numpages
scroll.draw()
+class NotificationApp(PagerApp):
+ NAME = 'Notifications'
+
+ def __init__(self):
+ super().__init__('')
+
+ def foreground(self):
+ notes = wasp.system.notifications
+
+ id = next(iter(notes))
+ note = notes[id]
+ del notes[id]
+ self._msg = '{}\n\n{}'.format(note['title'], note['body'])
+
+ super().foreground()
+
class CrashApp():
"""Crash handler application.
diff --git a/wasp/apps/testapp.py b/wasp/apps/testapp.py
index a312530..e2e8828 100644
--- a/wasp/apps/testapp.py
+++ b/wasp/apps/testapp.py
@@ -16,7 +16,7 @@ class TestApp():
ICON = icons.app
def __init__(self):
- self.tests = ('Button', 'Crash', 'Colours', 'Fill', 'Fill-H', 'Fill-V', 'RLE', 'String', 'Touch', 'Wrap')
+ self.tests = ('Button', 'Crash', 'Colours', 'Fill', 'Fill-H', 'Fill-V', 'Notifications', 'RLE', 'String', 'Touch', 'Wrap')
self.test = self.tests[0]
self.scroll = wasp.widgets.ScrollIndicator()
@@ -70,6 +70,19 @@ class TestApp():
self._update_colours()
elif self.test.startswith('Fill'):
self._benchmark_fill()
+ elif self.test == 'Notifications':
+ if event[1] < 120:
+ wasp.system.notify(wasp.watch.rtc.get_uptime_ms(),
+ {
+ "src":"Hangouts",
+ "title":"A Name",
+ "body":"message contents"
+ })
+ else:
+ if wasp.system.notifications:
+ wasp.system.unnotify(
+ next(iter(wasp.system.notifications.keys())))
+ self._update_notifications()
elif self.test == 'RLE':
self._benchmark_rle()
elif self.test == 'String':
@@ -166,6 +179,10 @@ class TestApp():
for s in self._sliders:
s.draw()
self._update_colours()
+ elif self.test == 'Notifications':
+ draw.string('+', 24, 100)
+ draw.string('-', 210, 100)
+ self._update_notifications()
elif self.test == 'RLE':
draw.blit(self.ICON, 120-48, 120-32)
@@ -181,3 +198,6 @@ class TestApp():
draw.string('RGB565 #{:04x}'.format(rgb), 0, 6, width=240)
draw.fill(rgb, 60, 35, 120, 50)
+
+ def _update_notifications(self):
+ wasp.watch.drawable.string(str(len(wasp.system.notifications)), 0, 140, 240)
diff --git a/wasp/boards/pinetime/manifest.py b/wasp/boards/pinetime/manifest.py
index 0193fed..3a525e1 100644
--- a/wasp/boards/pinetime/manifest.py
+++ b/wasp/boards/pinetime/manifest.py
@@ -28,6 +28,7 @@ freeze('../..',
'fonts/sans24.py',
'fonts/sans28.py',
'fonts/sans36.py',
+ 'gadgetbridge.py',
'icons.py',
'ppg.py',
'shell.py',
diff --git a/wasp/gadgetbridge.py b/wasp/gadgetbridge.py
new file mode 100644
index 0000000..f1219dd
--- /dev/null
+++ b/wasp/gadgetbridge.py
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: LGPL-3.0-or-later
+# Copyright (C) 2020 Daniel Thompson
+"""Gadgetbridge/Bangle.js protocol
+
+Currently implemented messages are:
+
+ * t:"notify", id:int, src,title,subject,body,sender,tel:string - new
+ notification
+ * t:"notify-", id:int - delete notification
+ * t:"alarm", d:[{h,m},...] - set alarms
+ * t:"find", n:bool - findDevice
+ * t:"vibrate", n:int - vibrate
+ * t:"weather", temp,hum,txt,wind,loc - weather report
+ * t:"musicstate", state:"play/pause",position,shuffle,repeat - music
+ play/pause/etc
+ * t:"musicinfo", artist,album,track,dur,c(track count),n(track num) -
+ currently playing music track
+ * t:"call", cmd:"accept/incoming/outgoing/reject/start/end", name: "name", number: "+491234" - call
+"""
+
+import io
+import json
+import sys
+import wasp
+
+# JSON compatibility
+null = None
+true = True
+false = False
+
+def _info(msg):
+ json.dump({'t':'info', 'msg':msg}, sys.stdout)
+ sys.stdout.write('\r\n')
+
+def _error(msg):
+ json.dump({'t':'error', 'msg':msg}, sys.stdout)
+ sys.stdout.write('\r\n')
+
+def GB(cmd):
+ task = cmd['t']
+ del cmd['t']
+
+ try:
+ if task == 'find':
+ wasp.watch.vibrator.pin(not cmd['n'])
+ elif task == 'notify':
+ id = cmd['id']
+ del cmd['id']
+ wasp.system.notify(id, cmd)
+ elif task == 'notify-':
+ wasp.system.unnotify(cmd['id'])
+ else:
+ _info('Command "{}" is not implemented'.format(cmd))
+ except Exception as e:
+ msg = io.StringIO()
+ sys.print_exception(e, msg)
+ _error(msg.getvalue())
+ msg.close()
+
+
diff --git a/wasp/icons.py b/wasp/icons.py
index ff39f8a..2b55d9c 100644
--- a/wasp/icons.py
+++ b/wasp/icons.py
@@ -150,3 +150,16 @@ knob = (
b'\x05\xe2\x07\xe0\x08\xe0\t\xde\x0b\xdc\r\xda\x10\xd6\x13\xd4'
b'\x16\xd0\x1c\xc8\x10'
)
+
+# 2-bit RLE, generated from res/notification.png, 105 bytes
+notification = (
+ b'\x02'
+ b' '
+ b'\x0f\xc2\x1d\xc4\x1c\xc4\x19\xca\x14\xce\x11\xd0\x0f\xd2\x0e\xc5'
+ b'\x08\xc5\r\xc4\x0c\xc4\x0c\xc4\x0c\xc4\x0b\xc4\x0e\xc4\n\xc4'
+ b'\x0e\xc4\n\xc4\x0e\xc4\n\xc3\x0f\xc4\t\xc4\x10\xc3\t\xc4'
+ b'\x10\xc4\x08\xc4\x10\xc4\x08\xc4\x10\xc4\x08\xc4\x10\xc4\x08\xc3'
+ b'\x11\xc4\x07\xc4\x12\xc4\x06\xc4\x12\xc4\x06\xc4\x12\xc4\x05\xc5'
+ b'\x12\xc4\x05\xc4\x14\xc4\x03\xc5\x14\xc5\x02\xde\x01\xff\x01\x01'
+ b'\xde\x0f\xc4\x1d\xc2\x0f'
+)
diff --git a/wasp/main.py b/wasp/main.py
index 8680219..c1db792 100644
--- a/wasp/main.py
+++ b/wasp/main.py
@@ -2,4 +2,5 @@
# Copyright (C) 2020 Daniel Thompson
import wasp
+from gadgetbridge import *
wasp.system.schedule()
diff --git a/wasp/wasp.py b/wasp/wasp.py
index a9f1a30..5246718 100644
--- a/wasp/wasp.py
+++ b/wasp/wasp.py
@@ -25,7 +25,7 @@ from apps.clock import ClockApp
from apps.flashlight import FlashlightApp
from apps.heart import HeartApp
from apps.launcher import LauncherApp
-from apps.pager import PagerApp, CrashApp
+from apps.pager import PagerApp, CrashApp, NotificationApp
from apps.settings import SettingsApp
from apps.steps import StepCounterApp
from apps.stopwatch import StopwatchApp
@@ -101,6 +101,8 @@ class Manager():
self.quick_ring = []
self.launcher = LauncherApp()
self.launcher_ring = []
+ self.notifier = NotificationApp()
+ self.notifications = {}
self.blank_after = 15
@@ -202,13 +204,26 @@ class Manager():
if self.app != app_list[0]:
self.switch(app_list[0])
else:
- watch.vibrator.pulse()
+ if len(self.notifications):
+ self.switch(self.notifier)
+ else:
+ # Nothing to notify... we must handle that here
+ # otherwise the display will flicker.
+ watch.vibrator.pulse()
+
elif direction == EventType.HOME or direction == EventType.BACK:
if self.app != app_list[0]:
self.switch(app_list[0])
else:
self.sleep()
+ def notify(self, id, msg):
+ self.notifications[id] = msg
+
+ def unnotify(self, id):
+ if id in self.notifications:
+ del self.notifications[id]
+
def request_event(self, event_mask):
"""Subscribe to events.
diff --git a/wasp/widgets.py b/wasp/widgets.py
index 5d8cc9a..2fcac9e 100644
--- a/wasp/widgets.py
+++ b/wasp/widgets.py
@@ -9,10 +9,11 @@ shared between applications.
"""
import icons
+import wasp
import watch
from micropython import const
-class BatteryMeter(object):
+class BatteryMeter:
"""Battery meter widget.
A simple battery meter with a charging indicator, will draw at the
@@ -69,7 +70,33 @@ class BatteryMeter(object):
self.level = level
-class ScrollIndicator():
+class Notifier:
+ """Show if there are pending notifications."""
+ def __init__(self, x=8, y=8):
+ self._pos = (x, y)
+
+ def draw(self):
+ """Update the notification widget.
+
+ For this simple widget :py:meth:`~.draw` is simply a synonym for
+ :py:meth:`~.update`.
+ """
+ self.update()
+
+ def update(self):
+ """Update the widget.
+
+ For this simple widget :py:meth:~.update` does nothing!
+ """
+ draw = watch.drawable
+ (x, y) = self._pos
+
+ if wasp.system.notifications:
+ draw.blit(icons.notification, x, y, fg=0x7bef)
+ else:
+ draw.fill(0, x, y, 32, 32)
+
+class ScrollIndicator:
"""Scrolling indicator.
A simple battery meter with a charging indicator, will draw at the