You are here

Speedport W 723V (A) - automatischer Abruf der Anruferliste und Abgleich mit Google Contacts

Seit kurzem haben wir VDSL von der Telekom (yeah!) und deshalb einen neuen Router. Das Gerät ist ein Speedport W 723V A mit relativ vielen Funktionen. Unter anderem können DECT-Telefone direkt angebunden werden und brauchen dann keinen eigene Basisstation. Diese Funktion wollte ich gerne nutzen, weil beim Anschluss des Telefons über die analoge TAE-Buchse nur eine Leitung genutzt werden kann und außerdem die Sprachqualität über die analoge Schnittstelle eher mäßig war. Das Problem: Entgangene Anrufe werden nicht mehr an die Telefone übermittelt, sondern müssen über das Web-Interface nachgeschaut werden - inakzeptabel. Zumindest beim Typ A vom 723 V ist wohl schon der neue Standard CATiq-2.0 implementiert, mit dem die Anruferliste an die Telefone übertragen werden kann. Nur kennen unsere Gigaset C300 Telefone den Standard leider noch nicht. Statt neue Telefone zu kaufen machen wir das Ganze einfach selbst und viel besser: Das folgende Java-Programm ruft die Liste vom Router ab und sendet bei neuen Anrufen eine E-Mail an beliebig viele Empfänger. So bekommen wir sogar unterwegs mit, wenn wir zu Hause einen Anruf verpasst haben.

Download: speedportcallerid.jar

Wie immer alles auf eigene Gefahr! Das Programm speichert allerdings nichts und ändert auch keine Einstellungen im Router, so dass sich das Risiko in Grenzen hält :) Ob das Ganze auch mit dem Speedport W 723V B funktioniert, weiß ich nicht.

Der Aufruf funktioniert so:

Usage: java -jar speedportcallerid.jar <router password> <update interval> <e-mail adress> <e-mail password> recipient1 recipient2 ... recipientN [--contacts] [--debug] [-?|--help]

  <router password>
        the login password for the Speedport W 723V (A) router

  <update interval>
        the interval beetween runs in minutes

  <e-mail adress>
        Google mail address to send from

  <e-mail password>
        the login password for the Google account

  recipient1 recipient2 ... recipientN
        e-mail addresses to send notifications to

  [--contacts]
        retrieve contacts from Google account and resolve caller names

  [--debug]
        print debug messages

  [-?|--help]
        print help and exit

Konkret lasse ich einen cronjob laufen, der alle 15 Minuten dieses Skript aufruft:

#!/bin/sh
cd /media/usb/hidrive/speedport
sudo -u nobody java -Duser.timezone=Europe/Berlin -jar speedportcallerid.jar --contacts <ROUTER-PASSWORT> 15 <GMAIL-ADRESSE> <GMAIL-PASSWORT> <EMPFÄNGER1> <EMPFÄNGER2>

Der cronjob dazu sieht so aus:

*/15 * * * * /usr/local/bin/check-speedport.sh

Im Moment sind die Mail-Einstellungen auf den Gmail-Server festgelegt (StartTLS + Port 587) für andere Server muss der Code evtl. angepasst werden - vielleicht baue ich später dafür auch nochmal zusätzliche Parameter ein, wenn jemand Interesse hat.

Nachtrag: Namensauflösung mit Gmail-Kontakten

Das Programm tat was es soll, aber die Akzeptanz im Haushalt ließ zu wünschen übrig: "Woher soll ich denn wissen, wer das war, wenn da nur die Nummer steht!?" Na gut, ist wohl nicht so praktisch.
Man kann zwar im Router eine Telefonliste pflegen, aber leider wird die nicht für die Anzeige der Rufnummern auf der Weboberfläche verwendet. Außerdem müsste man dann sämtliche Kontakte nochmal im Speedport pflegen, was sicherlich auch nicht Sinn der Sache ist. Zum Glück hab ich ja ein Android-Handy und deshalb sämtliche Telefonnummern im Google-Account gespeichert. Man muss also nur nochmal nach den passenden Namen suchen, nachdem man die Rufnummern aus dem Speedport gefischt hat. Erledigt. Das Aufruf-Skript muss nur um den Parameter --contacts ergänzt werden (s.o.)

Code

package de.tilman.callerid;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;

import com.google.gdata.client.Query;
import com.google.gdata.client.contacts.ContactsService;
import com.google.gdata.data.contacts.ContactEntry;
import com.google.gdata.data.contacts.ContactFeed;
import com.google.gdata.data.extensions.PhoneNumber;
import com.martiansoftware.jsap.JSAP;
import com.martiansoftware.jsap.JSAPException;
import com.martiansoftware.jsap.JSAPResult;
import com.martiansoftware.jsap.Switch;
import com.martiansoftware.jsap.UnflaggedOption;

/**
 * This class retrieves the list of missed calls from a Speedport W 723V (A)
 * router and sends notifications to a list of e-mail addresses.
 *
 * @author Tilman Liero tliero@gmx.net
 */

public class CallerIdClient {

        private final static Logger log = Logger.getLogger(CallerIdClient.class);

        private static final String TEXT_UNKNOWN = "unbekannt";
        private static final String TEXT_MAILPREFIX = "Anruf von ";

        String password;
        int updateInterval;
        JSAPResult config;
        LinkedList<String[]> calls = new LinkedList<String[]>();
        String cookie = null;

        public CallerIdClient(JSAPResult config) {
                this.config = config;
                this.password = new String(Base64.encodeBase64(config.getString("router password").getBytes()));
                this.updateInterval = config.getInt("update interval");

                try {
                        log.info("logging in to Speedport router");
                        postUrl("https://speedport.ip/index/login.cgi", "Username=" + URLEncoder.encode("admin", "UTF-8") + "&" + "Password="
                                        + password);

                        log.info("retrieving call list");
                        getUrl("https://speedport.ip/auth/hcti_status_telanrl.php?cookie=SessionID_R3," + cookie);

                        log.info("logging out");
                        postUrl("https://speedport.ip/auth/logout.cgi?RequestFile=/pub/top_beenden.php&cookie=SessionID_R3," + cookie, "");

                        if (calls.size() > 0) {

                                List<ContactEntry> contacts = null;
                                if (config.getBoolean("check contacts")) {
                                        log.info("retrieving Google contacts");
                                        ContactsService service = new ContactsService("<var>SpeedportCallerIdClient</var>");
                                        service.setUserCredentials(config.getString("e-mail adress"), config.getString("e-mail password"));
                                        URL feedUrl = new URL("https://www.google.com/m8/feeds/contacts/default/full");
                                        Query myQuery = new Query(feedUrl);
                                        myQuery.setMaxResults(1000);
                                        ContactFeed resultFeed = service.query(myQuery, ContactFeed.class);
                                        contacts = resultFeed.getEntries();
                                }

                                log.info("sending notifications");
                                for (String[] call : calls) {
                                        String time = call[0];
                                        String caller = call[1];
                                       
                                        if (caller.equals(" ")) {
                                                caller = TEXT_UNKNOWN;
                                        }
                                        else {
                                                numberSearch:
                                                for (ContactEntry contact : contacts) {
                                                        for (PhoneNumber phone : contact.getPhoneNumbers()) {
                                                                if (phone.getPhoneNumber().endsWith(caller.substring(1))) {
                                                                        if (contact.getName().hasFullName()) {
                                                                                caller = contact.getName().getFullName().getValue() + " (" + caller + ")";
                                                                                log.info("found number in contact data for " + contact.getName().getFullName().getValue());
                                                                        }
                                                                        break numberSearch;
                                                                }
                                                        }
                                                }
                                        }

                                        sendNotification(time, caller);
                                }
                        }
                } catch (Exception e) {
                        log.error(e.getMessage(), e);
                }

                log.info("done");
        }

        private void getUrl(String urlString) throws Exception {
                log.debug("getUrl(" + urlString + ")");

                URL url = new URL(urlString);
                URLConnection connection = url.openConnection();

                showHeaders(connection);

                BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line;

                while ((line = in.readLine()) != null) {
                        if (line.startsWith("var in_call_list")) {
                                // analyze caller list

                                String s = line.substring(39);
                                log.debug("  " + line);

                                String[] result = s.split("new Array\\(");
                                for (int x = 0; x < result.length; x++) {
                                        // check for unanswered calls
                                        if (result[x].substring(result[x].lastIndexOf('"') - 8, result[x].lastIndexOf('"')).equals("00:00:00")) {
                                                log.debug("Unanswered call: " + result[x]);

                                                // determine call time
                                                int year = Integer.parseInt(result[x].substring(7, 11));
                                                int month = Integer.parseInt(result[x].substring(4, 6));
                                                int date = Integer.parseInt(result[x].substring(1, 3));
                                                int hour = Integer.parseInt(result[x].substring(12, 14));
                                                int min = Integer.parseInt(result[x].substring(15, 17));
                                                int sec = Integer.parseInt(result[x].substring(18, 20));

                                                Calendar callTime = Calendar.getInstance();
                                                callTime.set(year, month - 1, date, hour, min, sec);

                                                Calendar lastCheck = Calendar.getInstance();
                                                lastCheck.setTimeInMillis(System.currentTimeMillis() - updateInterval * 1000 * 60);

                                                if (callTime.after(lastCheck)) {
                                                        String time = result[x].substring(1, 20);
                                                        String number = result[x].substring(23, result[x].indexOf('"', 23));
                                                        calls.add(new String[] { time, number });
                                                }
                                        }
                                }

                        }
                        log.debug("  " + line);
                }
                in.close();

        }

        private void postUrl(String urlString, String payload) throws Exception {
                log.debug("postUrl(" + urlString + ", " + payload + ")");

                URL url = new URL(urlString);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("POST");
                connection.setInstanceFollowRedirects(false);
                connection.setDoInput(true);
                connection.setDoOutput(true);
                connection.setUseCaches(false);
                connection.setRequestProperty("Content-Length", String.valueOf(payload.length()));

                if (cookie != null) {
                        connection.setRequestProperty("Cookie", "SessionID_R3=" + cookie);
                }

                OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
                if (payload.length() > 0) {
                        writer.write(payload);
                }
                writer.flush();

                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

                for (String line; (line = reader.readLine()) != null;) {
                        if (line.startsWith("SessionID_R3")) {
                                cookie = line.substring(13);
                        } else {
                                if (line.equals("4"))
                                        log.error("  could not log in, Speedport adminstration account is currently in use");
                                else
                                        log.error("  received unexpected response: " + line);
                        }
                }

                writer.close();
                reader.close();

                showHeaders(connection);
        }

        private void showHeaders(URLConnection connection) {
                for (int i = 0;; i++) {
                        String headerName = connection.getHeaderFieldKey(i);
                        String headerValue = connection.getHeaderField(i);

                        if (headerName == null && headerValue == null) {
                                // No more headers
                                break;
                        }
                        if (headerName == null) {
                                // The header value contains the server's HTTP version
                                log.debug("  Server HTTP version, Response code: " + headerValue);
                        } else {
                                log.debug("  Header: " + headerName + ", Value: " + headerValue);
                        }
                }
        }

        private void sendNotification(String time, String caller) throws Exception {
                String host = "smtp.gmail.com";
                String from = config.getString("e-mail adress");
                String pass = config.getString("e-mail password");
                Properties props = System.getProperties();
                props.put("mail.smtp.starttls.enable", "true");
                props.put("mail.smtp.host", host);
                props.put("mail.smtp.user", from);
                props.put("mail.smtp.password", pass);
                props.put("mail.smtp.port", "587");
                props.put("mail.smtp.auth", "true");

                String[] to = config.getStringArray("recipient");

                Session session = Session.getDefaultInstance(props, null);
                MimeMessage message = new MimeMessage(session);
                message.setFrom(new InternetAddress(from));

                InternetAddress[] toAddress = new InternetAddress[to.length];

                for (int i = 0; i < to.length; i++) {
                        toAddress[i] = new InternetAddress(to[i]);
                }

                for (int i = 0; i < toAddress.length; i++) {
                        message.addRecipient(Message.RecipientType.TO, toAddress[i]);
                }

                message.setSubject(TEXT_MAILPREFIX + caller);
                message.setText(TEXT_MAILPREFIX + caller + "\n" + time);

                log.info("sending message for call from " + caller + " at " + time);

                Transport transport = session.getTransport("smtp");
                transport.connect(host, from, pass);
                transport.sendMessage(message, message.getAllRecipients());
                transport.close();
        }

        @SuppressWarnings("rawtypes")
        public static void main(String[] args) {
                BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d{ISO8601} - %m%n")));
                log.info("Starting CallerIdClient version 1.1.2");

                JSAP jsap = new JSAP();

                try {
                        UnflaggedOption routerPassword = new UnflaggedOption("router password").setStringParser(JSAP.STRING_PARSER)
                                        .setRequired(true);
                        routerPassword.setHelp("the login password for the Speedport W 723V (A) router");
                        jsap.registerParameter(routerPassword);

                        UnflaggedOption updateInterval = new UnflaggedOption("update interval").setStringParser(JSAP.INTEGER_PARSER)
                                        .setRequired(true);
                        updateInterval.setHelp("the interval beetween runs in minutes");
                        jsap.registerParameter(updateInterval);

                        UnflaggedOption fromAddress = new UnflaggedOption("e-mail adress").setStringParser(JSAP.STRING_PARSER).setRequired(
                                        true);
                        fromAddress.setHelp("Google mail address to send from");
                        jsap.registerParameter(fromAddress);

                        UnflaggedOption emailPassword = new UnflaggedOption("e-mail password").setStringParser(JSAP.STRING_PARSER)
                                        .setRequired(true);
                        emailPassword.setHelp("the login password for the Google account");
                        jsap.registerParameter(emailPassword);

                        UnflaggedOption recipients = new UnflaggedOption("recipient").setStringParser(JSAP.STRING_PARSER).setRequired(true)
                                        .setGreedy(true);
                        recipients.setHelp("e-mail addresses to send notifications to");
                        jsap.registerParameter(recipients);

                        Switch contactsSwitch = new Switch("check contacts").setLongFlag("contacts");
                        contactsSwitch.setHelp("retrieve contacts from Google account and resolve caller names");
                        jsap.registerParameter(contactsSwitch);

                        Switch debugSwitch = new Switch("debug").setLongFlag("debug");
                        debugSwitch.setHelp("print debug messages");
                        jsap.registerParameter(debugSwitch);

                        Switch helpSwitch = new Switch("help").setLongFlag("help").setShortFlag('?');
                        helpSwitch.setHelp("print help and exit");
                        jsap.registerParameter(helpSwitch);

                } catch (JSAPException e) {
                        log.fatal(e.getMessage(), e);
                        System.exit(-1001);
                }

                JSAPResult config = jsap.parse(args);

                if (!config.success() || config.getBoolean("help")) {
                        for (java.util.Iterator errs = config.getErrorMessageIterator(); errs.hasNext();) {
                                log.error("Error: " + errs.next());
                        }

                        log.error("Usage: java -jar speedportcallerid.jar " + jsap.getUsage() + "\n\n" + jsap.getHelp());
                        System.exit(-1002);
                }

                if (config.getBoolean("debug")) {
                        log.setLevel(Level.DEBUG);
                } else {
                        log.setLevel(Level.INFO);
                }
                log.info("Log level set to " + log.getLevel());

                new CallerIdClient(config);
        }
}
AttachmentSize
File speedportcallerid.jar3 MB