Apps für Android programmieren leicht gemacht!
Raspberry Pi - Bewegungsmelder - Zusätzliche Nachricht

Raspberry Pi – Bewegungsmelder – Zusätzliche Nachricht

In einem der Kommentare zu dem Artikel Raspberry Pi – Bewegungsmelder fragte Michael nach einem Weg eine zusätzliche Nachricht an die App aus diesem Artikel übergeben zu können. Leider ist dies im eigentlichen Sinne der App nicht vorgesehen.

Dennoch möchte ich Michael und damit euch allen nicht die Möglichkeit verwehren das Python Skript auf dem Raspberry Pi so anzupassen, dass man ohne Probleme zusätzliche und vorher nicht vorgesehene Nachrichten versenden kann, ohne dass die Android App zu einem Fehler kommt.

Zuerst sollten wir vielleicht klären, wie Nachrichten versendet werden.
Der allgemeine Befehl zum Senden von Nachrichten über den Pi lautet:

curl --header "[EUER API KEY]" --header Content-Type:"application/json" https://gcm-http.googleapis.com/gcm/send -d '{"to":"/topics/bewegung", "data": {"message": "{\"O\":\"Wohnzimmer - Fenster\",\"T\":\"147194700\"}"}}'

Wenn ihr euch den Anfang dieses Befehls anseht, dann heißt das erste Wort „curl“. Dies ist der Name des Programms, dass das Senden der Nachricht an Google übernimmt. Anschließend kommen ein paar Informationen für Google.

Nach „-d“ kommt die eigentliche Nachricht.
Unter „to:[…]“ steht jetzt „/topics/bewegung“. Dies bedeutet, dass Google eine Nachricht an alle Android Geräte versenden wird, die den selben API-Key innerhalb unserer App integriert haben. Das Topic (Thema) „bewegung“ weist unsere Android App dazu an zu wissen, worum es sich bei der Nachricht handelt und wie diese behandelt werden soll.

Unter „data:[…]“ kommt dann die Nachricht, die unsere App behandeln soll.

Unsere App ist darauf programmiert bei dem Topic „bewegung“ den Ort („O“) und die Zeit („T“) auszulesen und als Nachricht anzuzeigen. Fehlt eine dieser Informationen stürzt die App ab. Gibt es eine zusätzliche Information ignoriert unsere App diese.

 

Die Lösung

Die Lösung unseres Problems ist, dass wir bei dem Topic „bewegung“ alles so belassen wie es ist.
Möchten wir etwas anderes tun (z. B. nur einen Text ohne Zeit, als Benachrichtigung ausgeben), so müssen wir im Python Skript des Raspberry Pi ein anderes Topic wählen und eine neue Vorgehensweise der Android App zu diesem Topic programmieren.

Bevor wir also innerhalb der App irgendetwas mit der übergebenen Nachricht tun, so müssen wir erst einmal prüfen, um welches Topic es sich handelt. Nehmen wir mal an, dass wir wie im Kommentar nur einen Text ohne Zeit ausgeben wollen, dann wählen wir als 2. Topic mal „text“ aus.

 

Programmierung

Der neue Code zur Klasse GCMListener.class würde nun so ähnlich lauten.
Ersetze einfach den Code dieser Klasse mit diesem, aber erhalte die oberste Zeile mit dem PackageNamen:

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.NotificationCompat;

import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.google.android.gms.gcm.GcmListenerService;

import org.json.JSONException;
import org.json.JSONObject;

public class GcmListener  extends GcmListenerService {

    @Override
    public void onMessageReceived(String from, Bundle data) {
        String jsonStringMessage = data.getString("message");

        // Prüfe, ob es sich bei der Benachrichtigung um das Thema "bewegung" handelt.
        if (from.startsWith("/topics/bewegung")) {
            
            // Da es sich um das Topic "bewegung" handelte lesen wir aus der Nachricht nun Ort und Zeit aus.
            
            String ort = "Unbekannt";
            Long zeitUnix = 0L;
            DateFormat formatter;
            Date date = null;
            formatter = new SimpleDateFormat("HH:mm dd.MM.yyy");

            try {

                // JSON einlesen
                JSONObject message = new JSONObject(jsonStringMessage);

                // Lese die Zeit und den Ort aus und erstelle aus der Unix Zeit ein lesbares Datum.
                ort = message.getString("O");
                zeitUnix = message.getInt("T") * 1000L;
                date = new java.util.Date(zeitUnix);
                
                // Gib eine Benachrichtigung aus.
                sendNotification(ort + " - " + formatter.format(date), getIdentifier(ort));

            } catch (JSONException e) {
                e.printStackTrace();
            }

        }

        // Prüfe, ob es sich bei der Benachrichtigung um das Thema "text" handelt.
        if(from.startsWith("/topics/text")) {

            // Da es sich um das Topic "text" handelte lesen wir nun nicht mehr aus der Nachricht Ort und Zeit aus, sondern übergeben den Text direkt an die Funktion, die eine Benachrichtigung ausgibt.

            // Gib eine Benachrichtigung aus.
            sendNotification(jsonStringMessage, getIdentifier(jsonStringMessage));
            
            // Soll die Benachrichtigung immer wieder überschrieben werden und KEINE neue Benachrichtigung erzeugen, dann gib als zweiten Paramater immer nur eine 0 ein.
            // Dazu einfach in der nächsten Zeile die Kommentare "//" entfernen und darüber einfügen.
            // sendNotification(jsonStringMessage, 0);
            
        }

    }

    // Erstelle aus jedem Ort eine eindeutige ID, damit es für jeden Ort oder jeden Text eine eigene Banchrichtigung gibt.
    public int getIdentifier(String str) {
        try {
            int id = 0;
            for (Byte t : str.getBytes("UTF-8")) {
                id += t.intValue();
            }
            return id;
        } catch (UnsupportedEncodingException e) {
            return 0;
        }
    }

    // Gib eine Benachrichtigung aus.
    private void sendNotification(String message, int id) {
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
                PendingIntent.FLAG_ONE_SHOT);

        Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("Bewegungsmelder")
                .setContentText(message)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        notificationManager.notify(id, notificationBuilder.build());
    }
}

 

Registrierung

Zusätzlich zu dem vorher genannten Code müssen wir unsere App nun dazu anweisen, dass sie sich mit dem neuen Topic auch bei Google registriert. Ansonsten prüfen wir in unserer App nach einem Topic, welches wir niemals von Google erhalten werden.

Wir öffnen die Klasse RegistrationIntentService und fügen zu der Zeile

private static final String[] TOPICS = {"bewegung"};

noch das Topic „text“ hinzu:

private static final String[] TOPICS = {"bewegung", "text"};

Da die App nur bei erstmaligem Starten sich für Topics einträgt wäre der einfachste Weg, dass wir die App noch einmal deinstallieren und neu installieren.

Marvin

Ich bin 23 Jahre jung und studiere zurzeit Wirtschaftsinformatik an der Georg-August-Universität in Göttingen. Ich bin ein Mensch, der sich neben der Programmierung noch für tausend andere Dinge interessiert, die mal mehr und mal weniger verrückt sind. Vor allem aber bin ich Feuer und Flamme mit der Programmierung von eigenen kleinen Apps und Programmen, die mein Leben bereichern.

3 Kommentare

*Pflichtfeld

  • Hallo Marvin,
    Deine Beschreibungen sind klasse – gehen aber weit über das hinaus, was ich zu leisten in der Lage bin.
    Aus Spielerei verwende ich auf dem iPhone geofency. Mit den implementierten Webhooks fülle ich eine Datenbank auf meinem Pi. Ein weiteres PHP Script listet mir dann meinen Tagesablauf auf. (Ich sag ja: Spielerei)
    Bei uns ist es bei einer weiteren Fahrt (Dienstreise, Urlaub etc.) üblich, dem Partner ne kurze Info bzgl. der Ankunft zu geben. Jetzt könnte ich das mittels mail() in php lösen. Eine Mail wird jedoch nicht schnell identifiziert. Hilfreicher ist da schon eine Push-Nachricht. Da kam mir Dein Artikel gerade recht!
    Allerdings traue ich mich an das Fertigstellen einer ‚allgemeinen App zum Empfangen und Ausgeben von Pushnachrichten auf einem Android Smartphone‘ nicht heran. Da habe ich absolut zu viele Defizite und zu wenig Zeit zum Üben.
    So ganz einfach: In php – wenn ich die Daten aus dem Webhook von Geofency in die SQL Tabelle schreibe – kann ich ja weitere weitere Befehle schreiben, die ich sogar mit Variablen aus Geofency fülle. Wenn diese Info dann über Google an die App geht und diese dann ’nur‘ die komplette Nachricht pusht, wäre das eine tolle Idee. Das müsste doch viel einfacher sein als die GPIO Statusmeldungen. Bist Du in diese Richtung noch aktiv?
    DANKE
    Gruß
    KD

    • Hallo KD,

      leider muss ich dich enttäuschen. DIE eine Android App zum Empfang der Nachrichten gibt es leider nicht.
      Das Problem ist einerseits, dass man sich immer eigenhändig einen API-Key von Google beschaffen und ihn eigenhändig in die App einpflegen muss. Dadurch muss man diese App dann auch eigenhändig kompilieren. Andererseits ist die App aus dem Artikel schon stark komprimiert, deshalb auch die vielen zusätzlichen Artikel zu dieser App. lediglich 2-3 Abfragen könnte man entfernen.

      Gruß