Schwedische und neuseeländische Wählscheiben: AGI-Skript in PHP oder Python für Asterisk wandelt die gewählten Nummern um

In English

Wichtiger Hinweis: Leider verfälschen die Google-Übersetzungen die Quellcodes. Bitte öffnet den Quellcode in einem anderen Fenster, wie es das nachstehende Bild zeigt. Von dort lassen sich die Quellcodes auch leichter herauskopieren:

1.11.2022 (zuletzt am 8.02.2023 aktualisiert)

Schwedische Wählscheiben besitzen gegenüber den meisten Wählscheiben auf der Welt eine abweichende Anordnung der Ziffern. Diese sind um eine Stelle entgegen des Uhrzeigersinns verrutscht dargestellt. Wer sich an den aufgedruckten Ziffern eines schwedischen Wählscheibentelefons orientiert, wählt falsch, wenn das Telefon z.B. an einer üblichen Telefonanlage, an einem Asterisk-Server oder an einer FritzBox angeschlossen ist.

Dieser Artikel ist so gestaltet, dass man keine Kenntnisse über PHP und AGI mitbringen muss. Lediglich Grundkenntnisse über Linux und Asterisk sind Voraussetzung. Dieser Artikel dient auch dazu, die ersten Schritte mit AGI-Skripten zu erleichtern und Fallstricke zu erkennen, damit es ohne Erfahrung gelingt, vorgefertigte Skripte in Asterisk erfolgreich einzubinden. Deshalb ist hier einiges weitschweifend mehrfach erklärt und mit Beispielen untermauert.

Links ein deutsches Telefon, rechts ein schwedischer Apparat (Ericsson Dialog), auf dem die aufgedruckten Ziffern um eine Stelle entgegen des Uhrzeigersinns verschoben sind. Die Ericsson Dialog wurden auch in die USA exportiert und erhielten dafür die übliche Nummernscheibe.

Das Problem: Die Mechanik der schwedischen und übrigen Wählscheiben ist im Prinzip gleich. Nur der Aufdruck der Ziffern ist abweichend von den üblichen Zifferblättern, wie sie mit wenigen Ausnahmen in den allermeisten Ländern der Welt vorkommen.

Schwedisches Ziffernblatt: 9876543210
    Übliches Ziffernblatt: 0987654321

Orientiert sich jemand an einem schwedischen Ziffernblatt, wird eine falsche Nummer gewählt. Diese falsche Nummer muss wieder Ziffer für Ziffer umgewandelt werden, damit die Telefonanlage die korrekte Nummer wählt. Beispiel: Jemand orientiert sich an dem schwedischen Ziffernblatt und wählt die Nummer 089 123. Tatsächlich wählt er dann die Nummer 190 234. Diese Nummer muss das Programm wieder korrigieren. Die Ziffer 0 wird zur 9. Bei den restlichen Ziffern von 1 bis 9 ist immer eine 1 abzuziehen.

Das Problem mit der schwedischen Wählscheibe ist ebenfalls hier beschrieben worden:

Eine schwedische Wählscheibe hat eine abweichende Anordnung der Ziffern, was das Wählen erschweren kann, wenn das Telefon zum Beispiel an einer FritzBox angeschlossen ist.
Wie bedient man ein schwedisches Wählscheibentelefon? – 23.10.2022: Schwedische Wählscheibentelefone besitzen ein unübliches Ziffernblatt. Die aufgedruckten Ziffern sind um eine Stelle entgegen des Uhrzeigersinns verschoben. Das macht das Wählen an einer normalen Nebenstellenanlage oder im Rest der Welt außerhalb Schwedens zum Problem. Wie wählt man unter diesen erschwerten Bedingungen die richtigen Nummern? – weiter

Softwarelösung für Asterisk-Telefonanlagen: Nun gibt es eine Softwarelösung für die Asterisk-Telefonanlagen in Form eines kleinen PHP-Programms, das mit Hilfe von AGI (Asterisk Gateway Interface) mit Asterisk Daten austauschen kann. Das nachfolgend hier vorgestellte Programm se2eu.php wurde von Rainer Dohmen, DL5PD, verfasst und er stellt es mit freundlicher Genehmigung zur Verfügung.

Notlösung: Eine aufgeklebte Papierscheibe mit der üblichen Anordnung der Ziffern auf einem schwedischen Wählscheibentelefon. Eine kleines PHP-Skript für Asterisk macht diese Bastellösung überflüssig.

Es wandelt alle Nummern, die entsprechend einer schwedischen Wählscheibe “falsch” gewählt wurden in die richtigen Nummern um.

Auf welcher Umgebung läuft das  Skript? Getestet wurde es unter anderem auf PHP 7.3, Asterisk 16.2.1 und auf einem Raspberry Pi 3B+ mit  Raspbian Buster (Debian) als Betriebssystem.

PHP nachinstallieren: Bei Raspbian Buster musste ich vorher noch PHP installieren, damit PHP-Skripte ausgeführt werden können.

sudo apt-get update
sudo apt-get install php

Dies führte zur Installation von PHP 7.2  Die installierte Version kann mit

php -v

in Erfahrung gebracht werden.

Wohin das Skript kopieren? Jetzt müssen wir noch wissen, in welchen Pfad wir die kleine PHP-Datei zu kopieren haben. Dies erfahren wir in der asterisk.conf. Sie befindet sich in dem Pfad, in dem sich die anderen Asterisk-Konfigurationsdateien befinden. Nachfolgend ein Ausschnitt aus der asterisk.conf:

[directories](!)
astcachedir => /var/cache/asterisk
astetcdir => /etc/asterisk
astmoddir => /usr/lib/asterisk/modules
astvarlibdir => /var/lib/asterisk
astdbdir => /var/lib/asterisk
astkeydir => /var/lib/asterisk
astdatadir => /var/lib/asterisk
astagidir => /var/lib/asterisk/agi-bin ; Hier steht der Pfad für die AGI-Skripte
astspooldir => /var/spool/asterisk
astrundir => /var/run/asterisk
astlogdir => /var/log/asterisk
astsbindir => /usr/sbin

[options]

Unter astagidir ist der Pfad eingetragen, in dem Asterisk die Skripte findet. Im obigen Beispiel also /var/lib/asterisk/agi-bin. Bei meiner Version Asterisk 16.2.1 sind die AGI-Skripte unter /usr/share/asterisk/agi-bin untergebracht.

Kodierung der Skripte: Auf jeden Fall müssen die Skripte wie in Linux üblich auch unter Windows mit UTF-8 kodiert sein, weshalb ich die Skripte unter Windows mit Notepad++ bearbeite.

Windows- und DOS-Textdateien für Linux umwandeln: Wer seine Skripte auf Windows bearbeitet oder erhält und dann auf den Raspberry  kopiert, wird die Programme nicht zum Laufen bekommen. Der Grund sind die unterschiedlichen Steuerzeichen für den Wagenrücklauf (CR) und den Zeilenumbruch (LF). Die Bezeichnungen stammen noch aus der Zeit, als mit UNIX-Rechnern auf Fernschreibern ausgedruckt wurde. Zum Glück gibt es eine Lösung, die auf https://phoenixnap.com/kb/convert-dos-to-unix beschrieben ist. Demnach müssen wir auf unserem Linux bzw. Raspbian mit dem Befehl

sudo apt install dos2unix

ein kleines Programm installieren, mit dem wir mit dem Befehl dos2unix die Windows- oder DOS-Dateien konvertieren können. Wie das genau geht, steht auf der vorhergehend genannten Seite. Zum Beispiel geht das für unser Beispiel wie folgt:

sudo dos2unix /usr/share/asterisk/agi-bin/se2eu.php

Dem Skript-Dateien Zugriffsrechte vergeben: Damit die Skripte auch laufen, müssen sie noch einige Zugriffsrechte erhalten und die Rechte für die Ausführbarkeit erhalten. Dies können wir radikal mit dem Befehl

sudo chmod 777 /usr/share/asterisk/agi-bin/se2eu.php

erreichen.

Diese Einstellung sollte man ebenfalls wählen, damit die Skripte unter Linux laufen.

Das PHP-Skript “se2eu.php” für die schwedische Wählscheibe: Die nachfolgenden Zeilen in eine Text-Datei kopieren und als se2eu.php umbenennen. Keine Schreibprogramme wie Word oder ähnliches verwenden. Die letzte Zeile darf keine Leerzeile sein.

#!/usr/bin/php -q
<?php

# Script to convert swedish dialpulses to european numbers
# 2022-10-31 by DL5PD, Rainer, dl5pd ätt darc punkt de
# tested with Asterisk 16 and PHP 7.4.30

# from Dialplan, call: same => n,AGI(se2eu.php,${EXTEN})
# with ${EXTEN} to be the number to be converted
# after Script execution, the converted number is found in ${EXTNEW}

# don't let this script run for more than 10 seconds
set_time_limit(10);

# turn off output buffering
ob_implicit_flush(false);

# retrieve all AGI variables from Asterisk
# and put them in array $agi

while (!feof(STDIN))
{
    $temp = trim(fgets(STDIN,4096));
    if (($temp === "") || ($temp == "\n"))
    {
        break;
    }
    $s = explode(":",$temp);
    $name = str_replace("agi_","",$s[0]);
    $agi[$name] = trim($s[1]);
}

# first argument (number to be converted) is found in $agi[arg_1]
$testinput = $agi[arg_1];

#local debug
#$testinput = "015394";
# expected output: "904283"

# here is the conversion: 0 gets 9, anything else is -1
for ($i=0; $i < strlen($testinput); $i++) {
  if (is_numeric($testinput[$i]) and $testinput[$i] == 0) {
    $testinput[$i] = 9; }
  elseif (is_numeric($testinput[$i]) and $testinput[$i] > 0) {
    $helper = $testinput[$i];
    $helper -= 1;
    $testinput[$i] = $helper; }
}

#local debug
#echo $testinput;

# sending variable ${EXTNEW} to Asterisk with converted number
fwrite(STDOUT, "SET VARIABLE EXTNEW $testinput");
fflush(STDOUT);
?>

Die Wählregeln in der extensions.conf: Nun muss noch in der extension.conf von Asterisk das PHP-Skript “se2ueu.php” angesprochen werden. Es gibt verschiedene Lösungen:

[telefone]
; Test für schwedische Wählscheibentelefone
; Vorwahl 65 auf der schwedischen Wählscheibe
  exten => _76.,1,Answer()
  same => n,AGI(se2eu.php,${EXTEN:2})
  same => n,NoOP(Aus Script: ${EXTNEW})
  same => n,Goto(telefone,${EXTNEW},1)
  same => n,Hangup()

Das obige Beispiel eignet sich für die ersten Tests und ist im Context “telefone” der extensions.conf untergebracht, in der sich die extensions (Wahlregeln) für die meisten Telefone befinden. Wählt man auf der schwedischen Wählscheibe die 65 vor, dann wird die “falsch” gewählte Nummer umgewandelt und die Vorwahl wird abgeschnitten.

Ohne Vorwahl geht das Wählen auch: Es geht auch ohne Vorwahl. Dafür schaffen wir einen neuen Context, den ich im Beispiel sm_telefone genannt habe.

[sm_telefone]
include => telefone
exten => _X.,1,Answer()
same => n,AGI(se2eu.php,${EXTEN})
same => n,NoOP(Aus Script: ${EXTNEW})
same => n,Goto(telefone,${EXTNEW},1)
same => n,Hangup()

Das ist der gesamte Inhalt des Contextes sm_telefone.

Telefonanschlüsse für schwedische Wählscheibentelefon erhalten in der sip.conf ebenfalls den Context sm_telefone. Nachfolgend ein Ausschnitt aus der sip.conf für den Telefonanschluss 1483:

[1483] ; Dagobert Duck
type=friend
context=sm_telefone  ; Hier ist der Context einzutragen.
secret=money_makes_the_world_go_round
host=dynamic
username=1483
canreinvite=no
port=5064
dtmfmode=rfc2833
callerid = "Dagobert Duck" <1483>

Alle schwedischen Wählscheibentelefone erhalten also in der sip.conf den Context sm_telefone. Dadurch werden sämtliche mit ihnen gewählten Nummern umgewandelt. Eine Vorwahl ist nicht mehr nötig. Nun hat man beim Wählen fast das Gefühl in Schweden des letzten Jahrhunderts zu sein.

Was passiert bei der Ausführung des AGI-Skripts: Damit wir in der Asterisk-Konsole sehen können, was beim Ausführen des AGI-Skripts passiert, müssen wir das Debugging für AGI aktivieren:

agi set debug on

Wählen wir vom schwedischen Wählscheibentelefon 1002 das “normale” Telefon 1012 an, dann ist in der Asterisk-Konsole folgendes zu beobachten:

Das passiert in der Asterisk-Konsole, wenn die schwedische 1002 die deutsche 1012 anwählt.

An der Leitung für das schwedische Telefon hängt noch ein normales Telefon: Damit man vom normalen Telefon, das parallel zum schwedischen Telefon angeschlossen ist, beim Wählen nicht umrechnen muss, kann man wieder mit  Vorwahlen arbeiten:

[sm_telefone]
include => telefone
exten => _X.,1,Answer()
  same => n,AGI(se2eu.php,${EXTEN})
  same => n,NoOP(Aus Script: ${EXTNEW})
  same => n,Goto(telefone,${EXTNEW},1)
  same => n,Hangup()
  
  exten => _65.,1,Answer()
  same => n,NoOP(Ziel-Nummer: ${EXTEN:2})
  same => n,Goto(telefone,${EXTEN:2},1)
  same => n,Hangup()

Im obigen Beispiel (Context sm_telefone der extensions.conf) bewirkt die Vorwahl 65, dass das AGI-Skript nicht zum Einsatz kommt. Alle Nummern, die mit 65 beginnen, werden normal gewählt und die ersten zwei Ziffern werden entfernt.

Und hier das PHP-Skript mit ChatGPT nach Python 2.7 umgewandelt: Der Vorteil ist, das in Raspbian Python schon installiert ist.

Es ist ein mit der Hilfe von ChatGPT geschriebenes AGI-Script auf der Basis von Python 2.7. Es wurde erfolgreich auf Asterisk 1.6.2 getestet. Es wandelt die mit einer schwedischen Wählscheibe gewählten Ziffern in die richtigen Ziffern um, wie sie in den meisten Ländern der Welt (z.B. Deutschland, USA) verwendet werden.

#!/usr/bin/python
import sys

agi = {}

while True:
    temp = raw_input().strip()
    if temp == '':
        break
    s = temp.split(':')
    name = s[0].replace("agi_", "")
    agi[name] = s[1].strip()

testinput = agi['arg_1']
result = ""

for i in range(len(testinput)):
    if testinput[i].isdigit():
        if int(testinput[i]) == 0:
            result += '9'
        else:
            result += str((int(testinput[i]) - 1) % 9)

print("SET VARIABLE EXTNEW " + result)
sys.stdout.flush()

Wie habe ich das gemacht? Ich habe die Kommentarzeilen das PHP-Programms von DL5PD entfernt und ChatGPT um folgendes gebeten: “Bitte wandle dieses AGI-Script in Python 2 um.” Darunter habe ich das PHP-Skript mit den entfernten Kommentarzeilen einkopiert.

Das Ergebnis war, dass es funktionierte, aber sich bei der 9 verrechnete. Ich musste dann ChatGPT nochmals bitten: “Kannst du mir dieses agi-skript so abändern, dass die eingegebenen ziffern wie folg konvertiert: aus 1 wird 0, aus 2 wird 1, aus 3 wird 2, aus 4 wird 3, aus 5 wird 4, aus 6 wird 5, aus 7 wird 6, aus 8 wird 7, aus 9 wird 8, aus 0 wird 9”.

Nachfolgend in den Screenshots die von ChatGPT verfassten Kommentaren zu den einzelnen Programmschritten:

Screenshot von ChatGPT
Screenshot von ChatGPT
Screenshot von ChatGPT

Das dann von ChatGPT erstellte Programm war sofort fehlerfrei. Ich habe dann den Code in eine Text-Datei mit dem Namen swedial.py kopiert und musste die extensions.conf für den Aufruf des  AGI-Scripts anpassen:

exten => _X.,1,Answer()
 ; same => n,AGI(se2eu.php,${EXTEN})
  same => n,AGI(swedial.py,${EXTEN})
  same => n,NoOP(Aus Script: ${EXTNEW})
  same => n,Goto(janson,${EXTNEW},1)
  same => n,Hangup()

Das war es im Prinzip.

Mit dem Python-Programm svedial.py wird von einer schwedischen Wählscheibe die Nummer 1023 angerufen.

Hier das AGI-Skript in Python 2.7 für neuseeländische Wählscheiben: ChatGPT war so freundlich und hat das Script für neuseeländische Wählscheiben angepasst. Es klappte nach dem zweiten Anlauf. Das Schema ist so simpel, dass man es selbst ohne Programmierkenntnisse für alle möglichen und unmöglichen Wählscheiben anpassen kann.

#!/usr/bin/python
import sys

agi = {}

while True:
    temp = raw_input().strip()
    if temp == '':
        break
    s = temp.split(':')
    name = s[0].replace("agi_", "")
    agi[name] = s[1].strip()

testinput = agi['arg_1']
result = ""

for i in range(len(testinput)):
    if testinput[i].isdigit():
        if int(testinput[i]) == 0:
            result += '0'
        elif int(testinput[i]) == 1:
            result += '9'
        elif int(testinput[i]) == 2:
            result += '8'
        elif int(testinput[i]) == 3:
            result += '7'
        elif int(testinput[i]) == 4:
            result += '6'
        elif int(testinput[i]) == 5:
            result += '5'
        elif int(testinput[i]) == 6:
            result += '4'
        elif int(testinput[i]) == 7:
            result += '3'
        elif int(testinput[i]) == 8:
            result += '2'
        elif int(testinput[i]) == 9:
            result += '1'

print("SET VARIABLE EXTNEW " + result)
sys.stdout.flush()

Auf https://www.telmuseum.de/telnz.htm ist ein neuseeländisches Wählscheibentelefon abgebildet.

Telefon mit einer Wählscheibe für Neuseeland. (Bildquelle: https://commons.wikimedia.org/wiki/File:New_Zealand_Rotary_Telephone.jpg )
Eine improvisierte neuseeländische Wählscheibe, um das Python-AGI-Script testen zu können.

Mit AGI für Asterisk ist fast alles möglich: Mit AGI lassen sich übrigens nicht nur PHP-Skripte in Asterisk einbinden. Es funktionieren auch Skripte, die mit Python, Perl und viele weitere Programmiersprachen verfasst sind. Für viele Programmiersprachen stehen AGI-Bibliotheken zur Verfügung, die den Programmieraufwand reduzieren. Auf jeden Fall befindet man sich auf einem sehr spezialisierten Umfeld.

Der Programmier-Code ist fehlerfrei. Trotzdem läuft das AGI-Skript nicht. Warum verflixt noch einmal?
AGI-Skripte für Asterisk unter Windows editieren – Was ist zu beachten? – 9. März 2023: Windows ist weit verbreitet. Deshalb schreiben nicht wenige ihre AGI-Skripte auf Windows, um sie dann in ihren Raspberry Pi zu kopieren. Wer einige Dinge nicht beachtet, wird die Skripte nicht zum Laufen bekommen. Hier sind die Fallstricke beschrieben, die mich viel Zeit gekostet haben. Die Beispiele beziehen sich auf Python. Sie haben für andere Programmiersprachen die entsprechende Gültigkeit. – weiter

Auf einem Raspberry Pi lässt sich ein kompletter Telefonserver betreiben, der seinen Teilnehmern über Landesgrenzen hinweg kostenloses Telefonieren von unterwegs oder daheim ermöglicht.
Asterisk-Telefonserver auf einem Raspberry Pi – Installation, Konfiguration, Programmierung, SIP, IAX2, AGI-Skripte, Sicherheit und Tipps zum praktischen Betrieb – 2.11.2022: Diese Seite richtet sich an jene, welche einen Asterisk-Telefon-Server auf einem Raspberry Pi betreiben möchten und später ein kleines Netzwerk aus Asterisk-Servern planen, um ein eigenständiges Telefonnetz aufzubauen. Los geht es mit der Installation von Raspbian und Asterisk auf einem Raspberry Pi und dann nach Lust und Laune immer tiefer in die Programmierung von Asterisk. Die Themen werden laufend erweitert.

Selbstverständlich muss es nicht unbedingt ein Raspberry Pi sein. Andere Linux-Rechner gehen auch. – weiter