A Beeminder client for Emacs.
Beeminder is a self-motivation web service. It is rather clever, and I should not waste time trying to explain it here – go to their website to learn more about it.
However, their default web interface sucks, and while the official Android app is a lot better, it still has a few drawbacks. The biggest one is that it is not integrated with Emacs. This Elisp library fixes that problem (and a few others, like lack of goal sorting and filtering).
This library is still under development, and everything (including keybindings) may change.
Put beeminder.el
somewhere Emacs can load it and (require
'beeminder)
. Try M-x customize-group RET Beeminder RET
. Two
things you have to set up are: your Beeminder username
(beeminder-username
) and authorization token
(beeminder-auth-token
– sign in and head to
https://www.beeminder.com/api/v1/auth_token.json to retrieve it.) The
option beeminder-goal-pp-format
is responsible for how each goal is
displayed; customize it to change the look of the list.
You need to install packages request
and anaphora
(e.g. using the
built-in Emacs package manager) in order to use beeminder.el
.
The entry point is M-x beeminder-list-goals
. After a while, you
should see your goals. Press h
to see what’s available. Refresh
the list with C-l
. Reload your goals with g
. Quit with q
.
Most commands are self-explanatory (and if not, their docstrings
explain what they do). Note: the dates are calculated with the
assumption that hours after midnight, but before 6:00am, still belong
to today! See the option beeminder-when-the-day-ends
for how to
change that.
Currently, you can sort goals according to their “losedate”
(derailment time) or “midnight” setting. These two variants are
enabled by the functions beeminder-sort-by-losedate
(bound to l
)
and beeminder-sort-by-midnight
(bound to m
).
The main drawback of the official clients is that you can’t easily
hide goals which are of no interest for you at the moment. With
Emacs, you can. Filtering commands are located on prefix f
.
You can filter goals by derailment time (NUMBER-OF-DAYS f d
, number
of days defaults to 3), by how much time is left to the goal’s
“midnight” (HOURS f u
, defaults to 8), or by how much percent of
daily rate you did today (PERCENTAGE f t
, defaults to 100). What
“today” means is governed by options beeminder-when-the-day-ends
(which see) and beeminder-use-goal-midnight-today-values
(which
see). TL;DR: with the default setting, “today” is the stretch of time
between 6:00am and 5:59am the next day. With these options, you can
change that hour or make the notion of “today” depend on the goal’s
“midnight” setting.
You can also set a zero or negative argument to any filtering command.
Try it to see what happens; beeminder.el
tries to do the right
thing for any filter.
(Currently, you can use just d
, u
and t
keybindings for
filtering by derailment time, urgency and percentage of today’s work
done. However, this may change in the future.)
You can also “kill” individual goals, i.e., make them invisible, with
f k
. (This is also bound to C-k
for convenience). You can
“unkill” all killed goals with f y
(or C-y
), or show (in the
minibuffer) which goals are killed with C-u f y
.
If you’re like me, many of your goals are of the “do this every day”
category. I usually set the rate for such goals to 0.8 daily, and
enter 1.0 each day, and set the “max safe days” to e.g. 3 (of course,
you need Plan Bee for that). This way, I have a bit of leeway –
I can safely slip once every six days. On the other hand, if one of
these goals derails in, say, 2 days, it can easily get lost,
especially when filtering out all goals with due date e.g. later than
tomorrow. In order to avoid that, you can set the variable
beeminder-everyday-goals-list
in your init.el
to a list of slugs
of “everyday” goals (as symbols). These goals will be shown even if
their deadline is later than the derailment time filter setting. You
may toggle displaying them by pressing e
.
You might also want to save current filter settings for later
retrieval. This can be done with f s
. Saved goals can be retrieved
by f r
. While saving is not persistent across Emacs sessions, you
can (ab)use this feature to have your favorite filter settings enabled
for retrieval in init.el
by defining the variable
beeminder-saved-filters
. For instance, to be able to quickly
retrieve the goals which are derailing today, with the exception of
two of them, you can put this in your init.el
:
(setq beeminder-saved-filters '((killed uvi meta)
(losedate . 0)))
You can disable all filters with f c
. (This also saves the current
filters if no filter settings were saved previously.) Alternatively,
you can disable a particular filter with - KEY
(that is, minus sign
and the key which enables that filter).
Move the point to a goal and press RET
to submit a datapoint. If
you give a prefix argument, this will be the amount; if not, you will
be asked for it (default is 1). Prefix argument -
(a minus sign)
submits the datapoint with yesterday’s date. Prefix argument C-u
asks for a date. (Having Org-mode loaded works better, since then
org-read-date
is used. If you want to use this functionality – and
believe me, you do – and you happen not to use Org-mode, you can
(require 'org)
in your init.el
. You don’t have to install
anything, Org is shipped with Emacs.) After submitting, the goal is
dirty, i.e., there is a discrepancy between its state on the server
and in the client. Dirty goals are shown in italic and gray. Refresh
the goal list (by pressing g
) to “clean” them (it might require from
a few seconds to a few minutes to work, probably because of
Beeminder’s server overload).
It may happen that the goals which should lose their dirtiness do
not do it. (One situation when it can happen is when you submit
a datapoint of 0.) In such cases, you can call M-x
beeminder-clear-dirty-goals
to manually reset the “dirty” flag for
all goals.
Many actions (submitting goals, reloading goals, deleting or editing
datapoints) are logged. Press L
(beeminder-pop-log
) to see the
log and q
to exit it.
You can press TAB
with point on a goal to display more detailed
information about a goal in a separate window. This information
includes (by default) most data available in the API, plus a set of
recent datapoints. The user option beeminder-goal-template
holds
the template for displaying that; it is a string with embedded
keywords (or s-expressions) starting with the #
sign. The list of
supported keywords together with the way they should be interpreted is
kept in beeminder-goal-template-fields-alist
; any keyword not
present there is assumed to be a property of the goal
datastructure
(see https://www.beeminder.com/api#goal for details). You may also
embed arbitrary s-exps in the template (preceding them also with #).
The option beeminder-history-length
determines how many datapoints
are downloaded from the server. Its default value is 7, which means
a week’s worth of them. Pressing m
downloads more datapoints (with
a positive prefix argument, it downloads that many more days’ worth of
datapoints; with a negative prefix argument, it downloads datapoints
from number of days equal to the abolute value of the argument; with
prefix argument equal to zero, it downloads all datapoints; without
a prefix argument, it downloads datapoints from
beeminder-history-length
more days than displayed currently.)
You can press q
or TAB
again to quit the goal details window.
Pressing n
and p
will move you to the next and previous datapoint
(or N datapoints forward/backward with a prefix argument; notice that
you don’t need to press C-u
to enter prefix arguments here, too).
You can also press e
to edit the current datapoint. You will be
asked about the timestamp (again, using org-read-date
if available),
the value and the comment. In all three cases, the default is the
previous value; for the comment, you can also use the usual minibuffer
history commands like M-n
, M-p
or M-r
(see the node on
Minibuffer history in the Emacs manual). Pressing C-g
at any
moment cancels the editing.
Pressing d
deletes the current datapoint. Emacs will ask for
confirmation; use the option beeminder-confirm-datapoint-deletion
to
change this behavior.
Note that editing a datapoint does not mark the goal as dirty; the current design of dirtiness makes it rather hard to fix. Deleting a datapoint works properly in this regard.
There is (rather experimental) support for displaying graphs. Press
i
to download and view the graph for the current goal.
Pressing W
opens the current goal in a browser.
beeminder.el
supports two kinds of Org-mode integration: submitting
data on marking items as DONE or on clocking out. Both use Org
properties to set various things up. Org-mode integration can be
toggled with the beeminder-org-integration-mode
command; as the name
suggests, it is a (global) minor mode. You may also turn the two
features on or off independently by evaluating the functions
beeminder-org-done-submitting
or
beeminder-org-clock-out-submitting
, with a positive or a nonpositive
argument respectively.
Then, for each item you want to link to a Beeminder goal, set its
beeminder
property to done
or clock
, and its slug
property to
the goal slug. You might also want to set the
beeminder-org-inherit-beeminder-properties
option to t
to turn
property inheritance on for Beeminder-related stuff. (This is
probably most useful for clocking subtasks.)
If for some reason you want to confirm the submitting each time, you
may set the comment
property to ask
. Then, you will be asked for
a comment each time. Other possible values for the comment
property
are: time
(you will get a comment of the form via Org mode at
<time>
), headline
, which uses the headline as the comment, path
,
which uses the headline together with the whole path, and any other
string, which is then used as a comment. In the latter case, you can
use a few special markers in that string: %t
(ISO-8601 timestamp),
%h
(headline), %p
(headline with the path), %%
(literal “%”
sign). In the case of the absence of the comment
property,
beeminder-org-default-comment
is used.
Marking an Org heading as DONE can automatically submit a Beeminder
datapoint. For that, set the property beeminder
to done
and put
the goal slug in the slug
property. The amount of the datapoint will
be 1, though this can be overriden by setting the property amount
to
a number.
This feature probably makes the most sense for items scheduled with a repeater.
Another way of leveraging Org-mode’s features is submitting time of
clocked items for “do X for at least Y minutes”-type goals. For that,
set the beeminder
property to clock
and the slug
property to the
goal slug. Each time this particular item is clocked out, the number
of minutes is submitted as a Beeminder datapoint. Alternatively, you
may set the unit
property to hours
so that the value is divided by
60.
Since it may happen that you clock out some item when offline, you may
also place point at a particular clock line and trigger the submission
manually by M-x beeminder-org-submit-clock-at-point
.
If you clocked more items while you were offline, you may find the
command M-x beeminder-org-submit-all-clocks
useful. It submits all
clocks in the region (if the region is active) or in the current
subtree otherwise. For performance reasons, it submits only clocks
that ended less than beeminder-org-submit-all-clocks-default-minutes
minutes ago (by default 24 · 60 = 1440 minutes). This value can be
also changed using a numeric prefix argument.
Note that calling beeminder-org-submit-clock-at-point
multiple times
on the same clock line submits it only once. More precisely, the
idempotency key is constructed from the start and end times of the
clock item.
- More sorting/filtering options (per request).
- Displaying goal graphs.
- Loading more datapoints for selected goal.
- More statistics for a goal.
- Road editing (much less likely to be done).
Feel free to send bug reports and/or feature requests to me.