18.03.2023
Dieses kleine mit Python realisierte AGI-Skript sagt die Laufzeit des Linux-Rechners, auf dem Asterisk läuft, gerundet in Tagen an. Da mein Asterisk-Server selbstverständlich im Dauerbetrieb arbeitet, lässt sich ermitteln, wie viele Tage der letzte Stromausfall zurückliegt. Ist man nicht daheim, lässt sich durch einen einfachen Anruf feststellen, ob es kürzlich einen Stromausfall gab. Diese Information kann in bestimmten Fällen sehr nützlich sein. Deshalb gehört dieses Programm auf jede Nebenstellenanlage auf der Basis von Asterisk.
Zum Beispiel kann man vom Urlaubsort kontrollieren, wie lange der Stromausfall ungefähr war und ob die Lebensmittel im Kühlschrank und der Gefriertruhe zu verderben drohen. Außerdem ist die Laufzeit eines Asterisk-Servers ein Indiz für die Zuverlässigkeit und Verlässlichkeit des Telefondienstes.
Getestet wurde das Skript auf einem Raspberry Pi 3 B+ mit Raspbian Buster, Python 2.7 und Asterisk 16.2.1. Es trägt den Namen linux-runtime.py:
#!/usr/bin/env python # -*- coding: utf-8 -*- # 17:54 Freitag, 17. März 2023 linux-runtime.py Volker Lange-Janson import subprocess import sys import re import time # Nachfolgendes wird für die Kommunikation mit Asterisk benötigt: #################################################################### # Read the incoming AGI environment variables env = {} tests = 0; while 1: line = sys.stdin.readline().strip() if line == '': break key,data = line.split(':') if key[:4] != 'agi_': #skip input that doesn't begin with agi_ sys.stderr.write("Did not work!\n"); sys.stderr.flush() continue key = key.strip() data = data.strip() if key != '': env[key] = data sys.stderr.write("AGI Environment Dump:\n"); sys.stderr.flush() for key in env.keys(): sys.stderr.write(" -- %s = %s\n" % (key, env[key])) sys.stderr.flush() def checkresult (params): params = params.rstrip() if re.search('^200',params): result = re.search('result=(\d+)',params) if (not result): sys.stderr.write("FAIL ('%s')\n" % params) sys.stderr.flush() return -1 else: result = result.group(1) #debug("Result:%s Params:%s" % (result, params)) sys.stderr.write("PASS (%s)\n" % result) sys.stderr.flush() return int(result) else: error_code = re.search('result=(-?\d+)',params) if error_code: error_code = int(error_code.group(1)) sys.stderr.write("FAIL (unexpected result '%s', error code %d)\n" % (params, error_code)) sys.stderr.flush() return error_code else: sys.stderr.write("FAIL (unexpected result '%s')\n" % params) sys.stderr.flush() return -2 def saynumber (params): sys.stderr.write("SAY NUMBER %s \"\"\n" % params) sys.stderr.flush() sys.stdout.write("SAY NUMBER %s \"\"\n" % params) sys.stdout.flush() result = sys.stdin.readline().strip() return checkresult(result) def sayit (params): sys.stderr.write("STREAM FILE %s \"\"\n" % str(params)) sys.stderr.flush() sys.stdout.write("STREAM FILE %s \"\"\n" % str(params)) sys.stdout.flush() result = sys.stdin.readline().strip() checkresult(result) ################################################################## # Hier fängt das eigentliche Programm an time_duration = 1 time.sleep(time_duration) sayit("/usr/share/asterisk/sounds/en_US_f_Allison/hello") sys.stdout.flush() time_duration = 1 time.sleep(time_duration) # Laufzeit von Linux in Sekunden abrufen output3 = subprocess.check_output('cat /proc/uptime', shell=True) uptime_seconds = float(output3.split()[0]) # Laufzeit in Tagen umwandeln und runden uptime_days = int(uptime_seconds / (60 * 60 * 24)) ####################################################################### ####################################################################### sayit("/usr/share/asterisk/sounds/eigene/statusmeldungen/uptime_days") sys.stdout.flush() time_duration = 1 time.sleep(time_duration) saynumber(uptime_days); sys.stdout.flush() time_duration = 1 time.sleep(time_duration) ####################################################################### ####################################################################### sayit("/usr/share/asterisk/sounds/en_US_f_Allison/goodbye") sys.stdout.flush() time_duration = 1 time.sleep(time_duration) ####################################################################### # Beispiel, wie das Skript in der extensions.conf aufgerufen wird # durch die Wahl der 350 # # exten => 350,1,Noop(Linux-Laufzeit: "${CALLERID(name)}" <${CALLERID(num)}>) # same => n,Answer() # same => n,AGI(linux-runtime.py) # same => n,Hangup() # Sprachansagen: # Laufzeit dieses Asterisk-Servers in Tagen:
Es müsste sowohl auf Python 2.7 und 3.x laufen.
So wird das AGI-Skript in der extensions.conf aufgerufen, wenn man die 350 wählt:
# Beispiel, wie das Skript in der extensions.conf aufgerufen wird # durch die Wahl der 350 # # exten => 350,1,Noop(Aufruf Linux-Laufzeit von: "${CALLERID(name)}" <${CALLERID(num)}>) same => n,Answer() same => n,AGI(linux-runtime.py) same => n,Hangup()
Dann wird noch eine Soundfile für die Sprachansage benötigt: uptime.zip
**************************
Version mit Laufzeitangabe in Tagen und Stunden:
Diese Variante gibt die Tage und Stunden in einem vollständigen Satz wieder.
Beispiel einer fiktiven Ansage in Tagen und Stunden:
Das Python-AGI-Skript:
#!/usr/bin/env python # -*- coding: utf-8 -*- # Montag, 20. März 2023 linux-runtime.py Volker Lange-Janson # Diese Variante gibt die Laufzeit in Tagen und Stunden # in einem vollständigen Satz an import subprocess import sys import re import time # Nachfolgendes wird für die Kommunikation mit Asterisk benötigt: #################################################################### # Read the incoming AGI environment variables env = {} tests = 0; while 1: line = sys.stdin.readline().strip() if line == '': break key,data = line.split(':') if key[:4] != 'agi_': #skip input that doesn't begin with agi_ sys.stderr.write("Did not work!\n"); sys.stderr.flush() continue key = key.strip() data = data.strip() if key != '': env[key] = data sys.stderr.write("AGI Environment Dump:\n"); sys.stderr.flush() for key in env.keys(): sys.stderr.write(" -- %s = %s\n" % (key, env[key])) sys.stderr.flush() def checkresult (params): params = params.rstrip() if re.search('^200',params): result = re.search('result=(\d+)',params) if (not result): sys.stderr.write("FAIL ('%s')\n" % params) sys.stderr.flush() return -1 else: result = result.group(1) #debug("Result:%s Params:%s" % (result, params)) sys.stderr.write("PASS (%s)\n" % result) sys.stderr.flush() return int(result) else: error_code = re.search('result=(-?\d+)',params) if error_code: error_code = int(error_code.group(1)) sys.stderr.write("FAIL (unexpected result '%s', error code %d)\n" % (params, error_code)) sys.stderr.flush() return error_code else: sys.stderr.write("FAIL (unexpected result '%s')\n" % params) sys.stderr.flush() return -2 def saynumber (params): sys.stderr.write("SAY NUMBER %s \"\"\n" % params) sys.stderr.flush() sys.stdout.write("SAY NUMBER %s \"\"\n" % params) sys.stdout.flush() result = sys.stdin.readline().strip() return checkresult(result) def sayit (params): sys.stderr.write("STREAM FILE %s \"\"\n" % str(params)) sys.stderr.flush() sys.stdout.write("STREAM FILE %s \"\"\n" % str(params)) sys.stdout.flush() result = sys.stdin.readline().strip() checkresult(result) ################################################################## # Hier fängt das eigentliche Programm an time_duration = 1 time.sleep(time_duration) sayit("/usr/share/asterisk/sounds/en_US_f_Allison/hello") sys.stdout.flush() time_duration = 1 time.sleep(time_duration) # Ermittlung der Linux-Laufzeit, Umrechnung, Übergabe in Variablen # Laufzeit von Linux in Sekunden abrufen output3 = subprocess.check_output('cat /proc/uptime', shell=True) uptime_seconds = float(output3.split()[0]) # Laufzeit in Tagen umwandeln und runden uptime_days = int(uptime_seconds / (60 * 60 * 24)) # Laufzeit in Tagen und Stunden umwandeln und runden tag = int(uptime_seconds // (60 * 60 * 24)) stunde = int((uptime_seconds // (60 * 60)) % 24) # Die drei Soundfiles: # 1. The runtime of this Asterisk server is # 2. days and # 3. hours. ####################################################################### ####################################################################### # Eine For-Schleife wiederholt die Ansage 4 Mal. # Die Ansage besteht aus 3 Soundfiles runtime1, runtime2 und runtime3. # Dazwischen werden zwei # Nummern für die Tage und Stunden aufgesagt. for i in range(4): sayit("/usr/share/asterisk/sounds/eigene/statusmeldungen/runtime1") sys.stdout.flush() saynumber(tag); sys.stdout.flush() sayit("/usr/share/asterisk/sounds/eigene/statusmeldungen/runtime2") sys.stdout.flush() saynumber(stunde); sys.stdout.flush() sayit("/usr/share/asterisk/sounds/eigene/statusmeldungen/runtime3") sys.stdout.flush() time_duration = 3 time.sleep(time_duration) ####################################################################### # sayit("/usr/share/asterisk/sounds/eigene/statusmeldungen/uptime_days") # sys.stdout.flush() # time_duration = 1 # time.sleep(time_duration) # # saynumber(uptime_days); # sys.stdout.flush() # # time_duration = 1 # time.sleep(time_duration) ####################################################################### ####################################################################### sayit("/usr/share/asterisk/sounds/en_US_f_Allison/goodbye") sys.stdout.flush() time_duration = 1 time.sleep(time_duration) ####################################################################### # Beispiel, wie das Skript in der extensions.conf aufgerufen wird # durch die Wahl der 350 # # exten => 350,1,Noop(Serverlaufzeit aufgerufen von: "${CALLERID(name)}" <${CALLERID(num)}>) # same => n,Answer() # same => n,AGI(linux-runtime.py) # same => n,Hangup() #
Die drei Soundfiles: runtime1.wav, runtime2.wav und runtime3.wav
Download des Projekt mit allen notwendigen Dateien: Projekt Tage Stunden Server Runtime.zip
Zu beachten ist, dass bei dieser Ansage grammatikalische Fehler auftreten, da die Ansage immer von „days“ und „hours“, also von der Mehrzahl, ausgeht. Wer will, kann mit IF-Schleifen auch die Fälle in der Einzahl berücksichtigen und wenn Null Tage und Null Stunden auftreten. Ich denke jedoch mit diesem Schönheitsfehler leben zu können.
Andere Sprachen: Die Ansagen sind alle auf Englisch, da Englisch die am meisten verstandene Sprache auf der Welt ist. Selbstverständlich lassen sich auf Asterisk weitere Sprachen installieren und die Soundfiles in anderen Sprachen erstellen. Wie das geht, ist unter https://elektronikbasteln.pl7.de/erzeugen-von-telefon-ansagen-in-verschiedenen-sprachen erklärt.
*******
Weiterführend und ergänzend:
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 – |
Hurra! Mein Asterisk-Telefon-Server-Tutorial ist fertiggestellt – 9. März 2023: Das Tutorial besteht aus genau 40 Abschnitten und es ermöglicht Anfängern, auf einem Raspberry Pi einen eigenen Telefon-Server zu betreiben und diesen mit anderen Asterisk-Servern zu vernetzen. Zusätzlich sind zahlreiche wirkungsvolle Schutzmaßnahmen wie APIBAN und Fail2Ban beschrieben, um einen sicheren Betrieb zu gewährleisten. Asterisk läuft bei mir dank APIBAN jetzt wartungsfrei rund um die Uhr. Das Tutorial beinhaltet auch die Programmierung von AGI-Skripten auf Python und anderen Programmiersprachen, wodurch es fast keine Grenzen bei der Umsetzung gibt. – weiter – |