Erinnerungsassistent für Medikamenteinnahme mit Sprachausgabe

Erinnerungsassistent an Medikamenteinnahme

Motivation

Bevor Corona began, besuchten Ich und meine Familie meine Großeltern regelmäßig. Dabei erinnerten wir sie auch daran, ihre Medikamente zu bestimmten Uhrzeiten einzunehmen. Durch die Kontaktbeschränkung fiel dieser Umstand gänzlich weg. Da beide kein Smartphone oder Vergleichbares besitzen, erschien mir das nächstbessere ein Erinnerungsprogramm mit Sprachausgabe zu entwerfen.
Dieser Assistent ist voll konfigurierbar und hat zwei Stimmen – eine männlich, eine weiblich- um es dem Betroffenen einfacher zu gestalten.
Jeden Tag zur vorgegebenen Uhrzeit spricht der Assistent seine vorgegebene Phrase. Mit einem Service-Script ist der Assistent auf einem Raspberry Pi 3 komfortable lauffähig und startet nach dem Bootvorgang automatisch. Somit ist sichergestellt, das selbst nach einem Abschaltvorgang sich alles automatisch wieder einrichtet. Zum Einsatz kommt die MaryTTS Plattform.

Benötigte Komponenten

  1. Raspberry Pi
  2. Java ab Version 8
  3. Boxen

Maven

Um MaryTTS zu nutzen, benötigen wir sowohl die basis Bibliotheken als auch die passenden Stimmen. Stimmen gibt es zahlreiche, für viele Sprachen in jeweils männlich und weiblich und auch mit jeweils unterschiedlichen Klängen.

Basisbibliotheken

<dependency>
	<groupId>de.dfki.mary</groupId>
	<artifactId>marytts-runtime</artifactId>
	<version>5.2</version>
</dependency>
<dependency>
	<groupId>de.dfki.mary</groupId>
	<artifactId>marytts-common</artifactId>
	<version>5.2</version>
</dependency>
<dependency>
	<groupId>de.dfki.mary</groupId>
	<artifactId>marytts-signalproc</artifactId>
	<version>5.2</version>
</dependency>

Stimmen

<!-- Weiblich deutsch -->
<dependency>
	<groupId>de.dfki.mary</groupId>
	<artifactId>voice-bits1-hsmm</artifactId>
	<version>5.2</version>
</dependency>
<!-- Männlich deutsch -->
<dependency>
	<groupId>de.dfki.mary</groupId>
	<artifactId>voice-bits3-hsmm</artifactId>
	<version>5.2</version>
</dependency>

Abhängig von der verwendeten Sprache, muss über Maven die zugehörige Bibliothek hinzugefügt werden, erst dann ist diese auch verwendbar. Mehr Informationen dazu finden sich auf der offiziellen Webseite von Mary: http://mary.dfki.de.

Konfiguration

Die Konfiguration erfolgt über eine simple CSV-Datei. Die data.csv liegt standardmäßig im resources– Ordner.

Hour,Minute,Second,Repeatrate,Phrase,Voice
23,20,00,10000,Oma Aspirin einnehmen,bits3-hsmm
21,56,00,15000,Opa Vitamine einnehmen,bits1-hsmm

Die Überschriften der ersten Zeile sind für das Programm nicht relevant, da sie nur der Erklärung dienen.
Repeatrat
Die Zeit in Millisekunden, wann die Phrase wiederholt wird.
Phrase
Die ausgesprochene Nachricht des Assistenten
Voice
Die zu verwendende Stimme für diese Zeile.

Der Code

Das Programm besteht im wesentlichen aus zwei Komponenten: Die Sprachausgabe und dem Intervalltimer.

Sprachausgabe

Die Sprachausgabe wiederum besteht aus zwei Klassen: TextToSpeech und AudioPlayer.Während in der TextToSpeech Class die Konfiguration des AudioPlayers stattfindet, handelt dieser die Sprachausgabe.Begonnen wird mit dem Laden des MaryInterfaces.

marytts = new LocalMaryInterface();

Im nächsten Schritt wird ein Audiostream aus der vorgegeben Phrase generiert.

AudioInputStream ais = marytts.generateAudio(phrase);

Dieser generierte AudioInputStream wird nun an den erstellen AudioPlayer übertragen. Der Audioplayer läuft in einem eigenen Thread, somit wird ein laufendes Programm nie durch das Sprechen unterbrochen.
In der run-Methode des Audioplayers wird dieser Stream auf ein Stereo Ausgabeformat festgelegt, Format-Metainformationen ausgelesen und entsprechend in ein ByteArray eingelesen. Zum Abschluss empfängt die Javax-Sound Bibliothek das ByteArray und sendet es an das Tonausgabegerät.

@Override
public void run() {
    ais = new StereoAudioInputStream(ais, OutputFormat.STEREO.getValue());
    AudioFormat audioFormat = ais.getFormat();
    DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
    try {
        if (sdLine == null) {
            sdLine = ( SourceDataLine ) AudioSystem.getLine(info);
        }
        sdLine.open(audioFormat);
        sdLine.start();
        writeDataToLine();
        if ( ! stopThread) {
            sdLine.drain();
        }
    } catch (Exception e) {
       //Handle Exception
        return;
    } finally {
        sdLine.close();
    }
}
private void writeDataToLine() throws IOException {
    int readLine = 0;
    byte[] data = new byte[65532];
    while ( ( readLine != - 1 ) && ( ! stopThread )) {
        readLine = ais.read(data, 0, data.length);
        if (readLine >= 0) {
            sdLine.write(data, 0, readLine);
        }
    }
}

Intervalltimer

Der Intervalltimer besteht aus der Klasse ReminderTask und dem zugehörigen Datenobjekt Reminder.Reminder nimmt die CSV Einträge entgegen und wandelt diese in ein Calendarobjekt um.In der Klasse ReminderTask wird der Intervalltimer vorbereitet, sie nimmt die Text-to-Speech Referenz entgegen und sorgt dafür das eine Sprachausgabe angesteuert wird.

public ReminderTask(TextToSpeech tts, String speakResult) {
    this.tts = tts;
    this.speakResult = speakResult;
}
@Override
public void run() {
    tts.speak(speakResult);
}

Main

In der Mainmethode läuft alles zusammen, sie baut die Objekte zusammen, liest die CSV-Datei aus und startet schließlich den Intervalltimer.

public class Main {
    public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException {
        File currentDataFilePath = CsvFile.getCurrentDataFilePath();
        Reader fileReader = new InputStreamReader(new FileInputStream(currentDataFilePath), "ISO-8859-1");
        CsvReader csvReader = new CsvReader();
        ArrayList<Reminder> reminders = csvReader.readCsvData(fileReader);
        Timer timer = new Timer();
        for (Reminder reminder : reminders) {
            TextToSpeech tts = new TextToSpeech();
            tts.setVoice(reminder.voice);
            timer.scheduleAtFixedRate(new ReminderTask(tts, reminder.phrase), reminder.firstExecutionTime,
                reminder.repeatrate);
        }
    }
}

Raspberry Pi Service-Startscript

Um das Programm auf einem Raspberry zu starten, muss es zunächst in eine startbare .jar-Datei exportiert werden. Anschließend ist auf dem Raspberry die Csv-Datei zu erstellen. Dafür eignet sich z.B. der Nano-Editor.Das Programm verwendet standardmäßig den user.dir- Pfad. Auf dem Pi ist dies nicht automatisch der pi-user home folder.

[Unit]
Description=MedicationReminder service mit systemd
[Service]
ExecStart=/usr/bin/java -jar /home/pi/reminder/MedicationReminder.jar
Restart=always
[Install]
WantedBy=multi-user.target

Starten des Service

Startscript kopieren:

sudo cp /home/pi/reminder/MedicationReminder.service /etc/systemd/system/MedicationReminder.service

Script beim Booten starten

sudo systemctl enable MedicationReminder

Fazit

Durch die flexible Konfiguration können auch allgemeine Erinnerungen einprogrammiert werden. Mit einem Raspberry nimmt der ganze Assistent auch kaum Platz in Anspruch und ist eine kostengünstige Anschaffung ohne unnötig komplizierte Funktionen. Da die Konfigurationsdatei bequem über SSH bearbeitet werden kann, ist eine Fernwartung auch problemlos möglich. Darüber hinaus  ist eine Onlineanbindung auch nicht nötig und ermöglicht höchste Sicherheit.
Das gesamte Projekt ist auf Github zu finden.

Interne Schulungen in Cucumber & Selenium


In geschützter Umgebung lernt und festigt Ihr Team individuelles Wissen für Ihre aktuellen Anforderungen. Damit Ihr Projekt erfolgreich und wie geplant abgeschlossen wird.

Das könnte Sie auch interessieren

Unit Tests mit AI – Machinet Testfallerstellung

Unit Tests mit AI - Machinet Testfallerstellung Werden Tester bald durch KIs ersetzt? Spoiler: Nein In diesem Blogbeitrag möchte ich auf ein paar Grundlagen der KI...

Testautomatisierung mit Selenium Schulung

Testautomatisierung mit Selenium Schulung Schulungsbeschreibung Die Selenium Schulung vermittelt Basiswissen und die wichtigsten Aspekte zur Testautomatisierung. Nach...

Rolladensteuerung mit Loxone und Java

Automatische Rolladensteuerung mit Java und Loxone Motivation Loxone ist ein bekannter Anbieter für Smarthome Rolläden. Während sich über die mitgelieferte Oberfläche...

Apple Kalender auslesen mit Java

Apple Kalender mit Java auslesen Motivation Apple Kalender-Einträge sollen synchron auf meinem Java-Dashboard sowie auf sämtlichen anderen Geräten im Haus gepflegt...

Devops mit Cucumber, Jira und Xray

Devops mit Cucumber, Jira und Xray "Hat auf meiner Maschine funktioniert, ist jetzt Operating Problem“ Diese oder vergleichbare Aussagen sind nicht neu und...

ReSpeaker 4 LEDs mit Pi4J steuern

ReSpeaker 4 LEDs mit Pi4J steuern Motivation Für ReSpeaker im allgemeinen gibt es bereits zahlreiche Bibliotheken, auch einige die LEDs über die GPIO Pins ansteuern....

Sealed Classes mit Java JDK 17

Sealed Classes mit Java JDK 17 Was sind Sealed Classes? Mit dem kommenden Java Release 17 werden sogenannte Sealed Classes eingeführt. Diese waren zuvor bereits seit...

Fünf Tipps für besseren Testcode

Fünf Tipps für besseren Testcode Good Practices In diesem Blogpost zeige ich fünf Good Practices auf, um den eigenen Testcode zu verbessern. Aber wieso nur "Good...

API Testing mit Java und WireMock

API Testing mit Java und WireMock Motivation Ein bekanntes Problem: Öffentliche APIs werden abgefragt und liefern Daten in Form von JSON oder anderen Formaten zurück....