2020-06-09 22:29:00 +02:00
|
|
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
# Copyright (C) 2020 Daniel Thompson
|
|
|
|
|
2021-01-13 22:51:17 +01:00
|
|
|
"""Step counter
|
|
|
|
~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Provide a daily step count.
|
|
|
|
|
2023-03-07 02:02:07 +01:00
|
|
|
.. figure:: res/screenshots/StepCounterApp.png
|
2021-01-13 22:51:17 +01:00
|
|
|
:width: 179
|
|
|
|
|
|
|
|
The step counts automatically reset at midnight.
|
|
|
|
"""
|
|
|
|
|
2020-06-09 22:29:00 +02:00
|
|
|
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():
|
2021-01-13 22:51:17 +01:00
|
|
|
"""Step counter application."""
|
2020-06-09 22:29:00 +02:00
|
|
|
NAME = 'Steps'
|
|
|
|
ICON = icons.app
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
watch.accel.reset()
|
2021-02-25 09:00:03 +01:00
|
|
|
self._scroll = wasp.widgets.ScrollIndicator()
|
2020-11-14 13:25:32 +01:00
|
|
|
self._wake = 0
|
2020-06-09 22:29:00 +02:00
|
|
|
|
|
|
|
def foreground(self):
|
2020-11-14 13:25:32 +01:00
|
|
|
"""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
|
2020-12-30 11:29:37 +01:00
|
|
|
the past that we reconfigure the alarm.
|
2020-11-14 13:25:32 +01:00
|
|
|
|
|
|
|
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)
|
2020-11-04 20:00:37 +01:00
|
|
|
wasp.system.bar.clock = True
|
2021-02-25 09:00:03 +01:00
|
|
|
self._page = -1
|
2020-06-09 22:29:00 +02:00
|
|
|
self._draw()
|
2021-02-25 09:00:03 +01:00
|
|
|
wasp.system.request_event(wasp.EventMask.SWIPE_UPDOWN)
|
2020-06-09 22:29:00 +02:00
|
|
|
wasp.system.request_tick(1000)
|
|
|
|
|
2020-11-14 13:25:32 +01:00
|
|
|
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)
|
|
|
|
|
2021-02-25 09:00:03 +01:00
|
|
|
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)
|
|
|
|
|
2020-06-09 22:29:00 +02:00
|
|
|
def tick(self, ticks):
|
2021-02-25 09:00:03 +01:00
|
|
|
if self._page == -1:
|
|
|
|
self._update()
|
2020-06-09 22:29:00 +02:00
|
|
|
|
|
|
|
def _draw(self):
|
|
|
|
"""Draw the display from scratch."""
|
|
|
|
draw = wasp.watch.drawable
|
|
|
|
draw.fill()
|
|
|
|
|
2021-02-25 09:00:03 +01:00
|
|
|
if self._page == -1:
|
|
|
|
self._update()
|
|
|
|
wasp.system.bar.draw()
|
|
|
|
else:
|
|
|
|
self._update_graph()
|
2020-06-09 22:29:00 +02:00
|
|
|
|
|
|
|
def _update(self):
|
|
|
|
draw = wasp.watch.drawable
|
|
|
|
|
2021-02-25 09:00:03 +01:00
|
|
|
# Draw the icon
|
|
|
|
draw.blit(feet, 12, 132-24)
|
|
|
|
|
2020-10-21 22:29:15 +02:00
|
|
|
# Update the status bar
|
2020-11-04 20:00:37 +01:00
|
|
|
now = wasp.system.bar.update()
|
2020-06-12 09:52:36 +02:00
|
|
|
|
2021-02-25 09:00:03 +01:00
|
|
|
# Update the scroll indicator
|
|
|
|
scroll = self._scroll
|
|
|
|
scroll.up = False
|
|
|
|
scroll.draw()
|
|
|
|
|
2020-10-21 22:29:15 +02:00
|
|
|
# Update the step count
|
2020-06-09 22:29:00 +02:00
|
|
|
count = watch.accel.steps
|
|
|
|
t = str(count)
|
|
|
|
w = fonts.width(fonts.sans36, t)
|
|
|
|
draw.set_font(fonts.sans36)
|
2020-12-31 20:12:38 +01:00
|
|
|
draw.set_color(draw.lighten(wasp.system.theme('spot1'), wasp.system.theme('contrast')))
|
2020-06-09 22:29:00 +02:00
|
|
|
draw.string(t, 228-w, 132-18)
|
2021-02-25 09:00:03 +01:00
|
|
|
|
|
|
|
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
|
2021-03-01 21:03:07 +01:00
|
|
|
d = d // 4
|
2021-02-25 09:00:03 +01:00
|
|
|
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)
|