Bürowanderlust (Linux-Magazin, Oktober 2013)

Ein elektronischer Armreif namens "Up" der Firma Jawbone misst Tag und Nacht die Armbewegungen seines Trägers schließt daraus auf zurückgelegte Wegstrecken und nächtliche Schlafrhythmen. Fitnessforscher dürfen die gesammelten Daten nicht nur mit den offiziellen Smartphone-Apps, sondern auch mit Perl-Skripts und mittels der inoffiziellen Web-API abgreifen.

Um der allgemeinen Tendenz zur Bürgerverfettung entgegenzuwirken, trägt man heutzutage kleine Gadgets mit sich herum, die einen daran erinnern, wie wenig man sich doch bewegt. Stundenlang nur regungslos vor dem Bildschirm zu sitzen ist ungesund, statt dessen springt der Gesundheitsapostel schon mal auf, um einmal ums Karree zu gehen oder vielleicht sogar die Toilette im vierten Stock aufsuchen, welchen man selbstverständlich nicht im Aufzug, sondern schwer schnaufend übers Treppenhaus erreicht.

Abbildung 1: Der Armreif "Jawbone", der aus den Armbewegungen auf die Aktivitäten des Trägers schließt.

Ein elektronischer Armreif wie das Up-Band von der Firma Jawbone misst dabei die dabei gegangenen Schritte und zeigt später über die zugehörige App fürs Smartphone die zurückgelegte Wegstrecke und die verbrauchten Kalorien an. Dabei scheint das Gerät tatsächlich nur momentane Beschleunigungswerte zu messen, und diese zeitlich aufzukumulieren. Die geographische Position des Trägers ließe sich mit einem GPS-Chip ermitteln, aber der würde die kleine Batterie des Geräts wohl in kürzester Zeit leersaugen.

Entblößtes Innenleben

Auf Youtube hat ein Elektronik-Freak die äußere Gummihülle des Armbands aufgeschlitzt ([2]) und die wasserdicht eingeschweißten Komponenten ans Licht gebracht. Er fand ein halbes Dutzend kleinerer Platinchen mit SMD-Komponenten, aber außer einem Beschleunigungsmesser, der Lithium-Ion-Batterie inklusive Ladechips, einem Vibrator und Komponenten zum Andocken an den Audio-Port des Smartphones kam nichts zutage.

Abbildung 2: Das Smartphone zeigt die Daten an: Nicht genügend Schlaf, aber das Fitness-Ziel von 10.000 Schritten pro Tag wurde erreicht.

Der Armreif schickt also lediglich die zeitlich aufgereiten Beschleunigungsdaten an ein Machine-Learning-System, das aufgrund des Bewegungsmusters errät, welche Art von Bewegung vorlag (Gehen, Laufen, Fitnesstrainer), wieviele Schrittbewegungen der Träger dabei durchführte und was wohl die dabei zurückgelegte Wegstrecke war.

Abbildung 3: Auf Youtube hat ein Bastler den Armreif aufgeschlitzt und erklärt die Komponenten.

Schlaf der Gerechten

Wer den Reif auch nachts trägt, der kann sich am Morgen staunend seine Schlafmuster ansehen. Die App zeigt graphisch an, wann der Träger zu Bett ging, wann er eingeschlafen ist, wie oft er aufgewacht oder aufgestanden ist, und weckt ihn auf Wunsch mit sanftem Vibrieren innerhalb eines 20 Minuten langen Zeitfensters zu einem Zeitpunkt, an dem das Aufwachen am Schönsten ist. Zum Erfassen der Schlafrhythmen muss der User das Gerät allerdings vor dem Einschlafen manuell mittels eines Druckknopfs in den Schlafmodus versetzen. Bewegt sich der Träger dann lange Zeit nicht, ist er wohl tief eingeschlafen, liegen kleinere Bewegungen vor, ist er in eine leichtere Schlafphase eingetreten.

Abbildung 4: Erst um 1:30 ins Bett, dann um halb sieben kurz aufgewacht und gelesen, dann nochmal eingeschlummert und um acht aufgestanden.

Die Batterie im Band hält ungefähr eine Woche ohne Nachladen durch, und um die Daten auszulesen, zieht der User ein Stöpselchen an einem Ende des Bandes ab, um einen Audio-Stecker freizulegen. Diesen führt er in den Audioport seines Smartphones ein (Abbildung 5), startet die "Up"-App und das Band schickt wie ein Audiokoppler vom anno dunnemals die Bewegungsdaten an die App und damit an den Server in der Jawbone-Cloud. Von dort erhält dann die App wieder die aufbereiteten Daten zurück und zeigt sie graphisch ansprechend an (Abbildungen 2 und 4). Wer will, kann Freunde in sein "Team" einladen und einen Fitnesswettbewerb organisieren. Wenn man den Arbeitskollegen nicht auf die Nase binden möchte, dass man sich nächtelang unruhig im Bett hin- und herwälzt und nicht schlafen kann, lassen sich diese eher heiklen Informationsbissen im Bereich "Sharing" von der Publikation ausnehmen.

Abbildung 5: Steckt der Audiostecker des Armbands in der Buchse des Smartphones, während die App "Up" läuft, überträgt sie die Daten.

Offene Daten

Nun liegen die Schritt- und Schlafdaten nicht nur über die App, sondern auch in etwas roherer Form vor. Dazu nutzt das Skript in Listing 1 das CPAN-Modul WWW::Jawbone::Up und damit indirekt eine inoffizielle Web-API, die Bewegungsdaten in Perl-Skripts importiert. So darf der experimentierfreudige Fitness-Enthusiast zum Beispiel auf der Linux-Kommandozeile schnell mal seine bisher hochgeladenen Daten abfragen:

     $ ./uptest
     Mike Schilli walked 8.597km today
     Mike Schilli slept for 6h19m last night

Listing 1 zieht dazu erst das CPAN-Modul herein und loggt sich dann mit der connect-Methode in der Up-Cloud ein. Die Email-Adresse und das Passwort in den Zeilen 6 und 7 sind durch den bei Up registrierten User-Account zu ersetzen, den der User bei der Aktivierung des Bandes angelegt hat. Die Methode user() holt die persönlichen Daten des eingeloggten Users ein. Aus diesen extrahiert dann name() den registrierten Vor- und Nachnamen.

Listing 1: uptest

    01 #!/usr/local/bin/perl -w
    02 use strict;
    03 use WWW::Jawbone::Up;
    04 use DateTime::Duration;
    05 
    06 my $email = 'user@isp.com';
    07 my $pass  = 'changeme';
    08 
    09 my $up = WWW::Jawbone::Up->connect(
    10   $email, $pass );
    11 
    12 my $user  = $up->user();
    13 my $score = $up->score();
    14 
    15 print $user->name . ' walked ' . 
    16   $score->move->distance . 'km today', "\n";
    17 
    18 my $dtd = DateTime::Duration->new( 
    19   minutes => $score->sleep->asleep()/60 );
    20 
    21 print $user->name . " slept for ",
    22   $dtd->hours(), "h", int( $dtd->minutes() ), 
    23   "m", " last night\n";

Die Methode score() erhält die aufbereiteten Bewegungsdaten vom Up-Server. Die in den letzten 24 Stunden vom Träger zurückgelegte Wegstrecke in Kilometern kommt mit move()->distance() zutage. Die Schlafdauer liefert sleep()->asleep() in Sekunden. Die nachfolgende Zeile teilt den Wert durch 60, erhält so Minuten, und das CPAN-Modul DateTime::Duration errechnet daraus die vollen Stunden und überbleibenden Minuten. Bei Sekunden stellt es sich stur, da dies wegen Schaltsekunden in Sonderfällen fehlerhaft wäre.

Schön bunt

Mit der Fülle der verfügbaren Daten lassen sich nun allerhand Sperenzchen anstellen. Ansprechende Diagramme erfordern lediglich ein Grafikpaket wie Chart::Clicker (erhältlich auf dem CPAN, Dokumentation auf [3]), das sie ohne viel Handarbeit aber trotzdem ästethisch vollwertig generiert. Abbildung 6 zeigt zum Beispiel zwei Graphen in einem Schaubild: die während der letzten 24 Stunden aufkumulierte Wegstrecke in Lila und die in dynamisch festgelegten Intervallen gemessenen Schritte in gelb.

Variable Zeitfenster

Um Speicherplatz zu sparen, legt das Band Daten nämlich in unterschiedlich langen Messintervallen ab. Rührt sich lange Zeit nichts, wie zum Beispiel in der Nacht, wenn der Träger schläft, beträgt das Zeitfenster mit dem Zählwert für die registrierten Bewegungen schon mal eine Stunde oder mehr. In vollem Lauf hingegen schreibt der Armreif jede Minute einen Eintrag in den Speicher, der die Anzahl der Schritte festlegt. Der Up-Server berechnet daraus die geschätzte zurückgelegte Strecke, die durchschnittliche Geschwindigkeit, die bei der Aktivität verbrannten Kalorien und die Anzahl der Sekunden, in denen der User im Zeitintervall aktiv war.

Abbildung 6: Zurückgelegte Wegstrecke über die letzten 24 Stunden in Lila und die in dynamisch gewählten Messintervallen gezählten Schritte in Gelb.

All diese Informationen kann ein Skript wie Listing 2 über Methoden abgreifen und mit Hilfe des Chart-Pakets grafisch darstellen. Das Paket Chart::Clicker ([3]) vom CPAN erzielt formschöne Ergebnisse und ist außerdem so flexibel, dass es sogar wie in Abbildung 6 gezeigt zwei verschiedene Arten von Graphen in ein Schaubild packen kann: Die über die Zeitachse bislang zurückgelegten Kilometer packt ein Render-Engine namens Chart::Clicker::Renderer::Area in eine ausgemalte Fläche (lila in Abbildung 6) und die Schübe mit Step-Zählwerten zeigt eine Balkengrafik mit dünnen "Bars" in rot, ebenfalls über die Zeitachse.

Verschiedene Achsen

Von Haus aus stellt Chart::Clicker Daten in Form von X/Y-Koordinaten dar. Bestehen die X-Werte aus Datumsangaben, sorgt das Modul Chart::Clicker::Axis::DateTime dafür, dass die als Unix-Sekunden angegebenen Zeitwerte schön im Datums- oder Uhrzeitformat auf der X-Achse erscheinen.

Listing 2: bandchart

    01 #!/usr/local/bin/perl -w
    02 use strict;
    03 use Chart::Clicker;
    04 use Chart::Clicker::Renderer::Area;
    05 use Chart::Clicker::Renderer::Bar;
    06 use Chart::Clicker::Axis::DateTime;
    07 use Chart::Clicker::Data::DataSet;
    08 use Chart::Clicker::Data::Series;
    09 
    10 use WWW::Jawbone::Up;
    11 use DateTime::Duration;
    12 
    13 my $email = 'user@isp.com';
    14 my $pass  = 'changeme';
    15 
    16 my $up = WWW::Jawbone::Up->connect(
    17  $email, $pass );
    18 
    19 my @band  = $up->band( );
    20 
    21 my @time     = ();
    22 my @steps    = ();
    23 my @distance = ();
    24 my $total    = 0;
    25 
    26 for my $band ( @band ) {
    27   push @time, $band->time();
    28   push @steps, $band->steps();
    29 
    30   $total += $band->distance();
    31   push @distance, $total;
    32 }
    33 
    34 my $cc = Chart::Clicker->new( 
    35   width => 1000, height => 800 );
    36 
    37 my $def_ctx = $cc->get_context('default');
    38 
    39 my $steps = 
    40   Chart::Clicker::Data::Series->new(
    41     values => \@steps,
    42     keys   => \@time,
    43     name   => "Steps",
    44   );
    45 
    46 my $distance = 
    47   Chart::Clicker::Data::Series->new(
    48     values => \@distance,
    49     keys   => \@time,
    50     name   => "Distance",
    51   );
    52 
    53 my $ds = 
    54   Chart::Clicker::Data::DataSet->new( 
    55     series => [ $distance ] );
    56 my $ds_steps = 
    57   Chart::Clicker::Data::DataSet->new( 
    58     series => [ $steps ] );
    59 
    60 my $steps_ctx = 
    61   Chart::Clicker::Context->new(
    62     name => 'steps'
    63   );
    64 $cc->add_to_contexts( $steps_ctx );
    65 
    66 $ds_steps->context( "steps" );
    67 
    68 $cc->add_to_datasets( $ds );
    69 $cc->add_to_datasets( $ds_steps );
    70 
    71 my $dtaxis = 
    72   Chart::Clicker::Axis::DateTime->new(
    73     format      => "%H:%M",
    74     time_zone   => "America/Los_Angeles",
    75     position    => "bottom",
    76     orientation => "horizontal"
    77   );
    78 
    79 $def_ctx->domain_axis( $dtaxis );
    80 $steps_ctx->domain_axis( $dtaxis );
    81 
    82 my $ren = 
    83   Chart::Clicker::Renderer::Area->new(
    84     opacity => .7,
    85   );
    86 $ren->brush->width( 5 ) ;
    87 $def_ctx->renderer( $ren );
    88 $steps_ctx->renderer( 
    89   Chart::Clicker::Renderer::Bar->new( 
    90     opacity => .6, bar_width => 1 ) 
    91 );
    92 
    93 $cc->write_output( "chart.png" );

Das Skript holt sich in Zeile 19 die Rohdaten des Bandes während der letzten 24 Stunden mit der Methode band() ab. Wahlweise ließe sich eine beliebige Zeitraum festlegen. Zurück kommt ein Array von Einträgen, von denen jeder eine Referenz auf einen Hash enthält, der unter den Einträgen steps die Anzahl der pro Zeitperiode abgespulte Schrittzahl, unter distance die zurückgelegte Distanz, unter speed die Durchschnittsgeschwindigkeit während der Messperiode. In time steht der Zeitstempel, in active_time der Teil des Zeitfensters in dem der User sich bewegt hat und in calories die Anzahl der verbrannten Kalorien.

Mit den abgeholten Werten füllt das Skript dann drei Arrays: @time mit den Zeitstempeln im Unix-Format, @steps mit der Schrittzahl an diesen Zeitpunkten, und @distance, mit der bis zum jeweiligen Zeitpunkt zurückgelegten Gesamtstrecke.

Zeilen 40 und 47 erzeugen aus den Zeit/Step bzw. Zeit/Distanz-Kombinationen jeweils eine Datenserie vom Typ Data::Series. Ein Datensatz vom Typ Data::DataSet für einen Graph kann in Chart::Clicker mehrere Datenserien enthalten, aber die Zeilen 54 und 57 erzeugen jeweils einen Datensatz mit nur einer Serie für die beiden Graphen. Beide sollen verschieden dargestellt werden, einer als Area-Graph, und der andere als Balkengraphik, wenngleich im selben Diagramm.

Rendern im Kontext

Chart::Clicker arbeitet zum Rendern der Graphen in sogenannten Kontexten, von Haus aus ist "default" eingestellt und Zeile 37 holt das zugehörige Objekt. Zur Darstellung des zweiten Datensatzes definiert Zeile 61 einen weiteren Kontext namens steps und Zeile 64 fügt ihn zum System hinzu. Mit add_to_datasets() fügen dann die Zeilen 68/69 beide Datensätze zum Clicker-Objekt hinzu und die Methode domain_axis() weist beiden über den Kontext die Zeitachse Axis::DateTime zu, die die X-Werte von Datenreihen als Datumsangaben interpretiert und ebenso darstellt. Die Angabe %H:%M weist den Formatierer an, vom Unix-Datum jeweils nur die Stunde und die Minute in die Achse einzuzeichnen. Die Zeitzone "America/Los_Angeles" gilt nur für in San Francisco und Umgebung Ansässige, und ist entsprechend auf die lokalen Gegebenheiten anzupassen. Der Name der Zeitzone entspricht der dem Datumsmodul zugrunde liegenden Olson-Datenbank, und die Wikipedia-Seite auf [3] listet Werte für Europa und den Rest der Welt auf.

Abbildung 7: Beispiele für mit Chart::Clicker erzeugte Graphen (Quelle: [3])

Zwei in Einem

Der Zaubertrick mit den zwei verschiedenen Graph-Typen läuft nun über den Kontext: Zeile 87 weist dem Default-Kontext den Area-Renderer zu, während der Steps-Kontext in Zeile 88 den Bar-Renderer bekommt. Der Parameter opacity bestimmt in beiden Fällen die Durchsichtigkeit des Graphen: bei Werten um 0 ist sind die gemalten Linien und Flächen praktisch blickdicht, um 1 herum vollkommen durchsichtig. Die grafisch aufgemotzten Bewegungsdaten in Abbildung 6 enthüllen, dass ich nach der Arbeit schlechten Gewisssens noch schnell zum nahegelegenen Dolores-Park hinunter gelaufen bin, um noch einige Kilometer auf den Armreif zu spulen, denn an einem normalen Bürotag kommt kaum jemand auf die empfohlenen 10.000 Schritte. Während des abendlichen Lungerns vor dem Fernseher kamen erwartungsgemäß keine Kilomenter aufs Armband, aber nach ausgedehnter Bettruhe bis 8 Uhr morgens ging ich die 500 Meter bis zur privaten Bushaltestelle des Yahoo-Shuttles in San Francisco. Untertags erhöhte sich die gemessene Laufstrecke gemächlich, während der Träger in der Arbeit zwischen Meetings in unterschiedlichen Räumen oder gar Gebäuden hin- und herlief. Auf dem Weg von der Bushaltestelle zurück zur Wohnung gings's den Berg rauf und die Anzahl der zurückgelegten Schritte erhöhte sich erneut, bis sie sich auf etwa 10.000 für den Tag einfand.

Vorsicht, Baustelle!

Wer das Up-Band von Jawbone erwirbt, sollte sich mental darauf einstellen, dass es sich um ein noch nicht 100% ausgereiftes Produkt handelt. Die schlanke Bauweise des Armreifs beschwört erwartungsgemäß Qualitätsmängel herauf. Auf Diskussionsforen ist zu hören, dass die Armreife wohl öfter mal den Geist aufgeben, und dass dann der Kundenservice der Firma Jawbone nicht immer erwartungsgemäß reagiert. Die ersten Versionen des UP-Bands wiesen katastrophale Fehlerraten auf, neuere arbeiten zuverlässiger, aber auch heute hört man noch von Reifen, die ihre Batterie nicht richtig laden oder sich vollends aufhängen. Konkurrenz zum UP-Band ist in Form des "Fitbits" schon länger auf dem Markt. Das kleine Teil im Format eines USB-Sticks steckt man tagsüber in die Hosentasche, nachts muss man ihn aber in einem relativ breiten Stoffband am Arm festbinden.

Infos

[1]

Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2013/10/Perl

[2]

"Jawbone UP Pedometer Teardown", EEVblog #412, http://www.youtube.com/watch?v=sRjHAGsl6ws

[3]

Chart Clicker, http://gphat.github.io/chart-clicker/

[4]

Zeitzonen in der Olson-Datenbank: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones

Michael Schilli

arbeitet als Software-Engineer bei Yahoo in Sunnyvale, Kalifornien. In seiner seit 1997 laufenden Kolumne forscht er jeden Monat nach praktischen Anwendungen der Skriptsprache Perl. Unter mschilli@perlmeister.com beantwortet er gerne Ihre Fragen.