15.03.2023
Dieses AGI-Skript für Python ist eine nette Spielerei und sagt dem Anrufer die aktuelle Temperatur der CPU des Asterisk-Servers auf. Die Ansage wird wiederholt und ist in diesem Beispiel auf Englisch. Es gibt zwei Versionen. Dier erste Version sagt die Temperatur nur in Celsius auf. Die zweite Version sagt die Temperatur in Celsius und in Fahrenheit auf.
So ganz ist es doch keine Spielerei, wenn man per Anruf die Temperatur des Asterisk-Servers erfährt. Liegt sie im Bereich des Normalen, ist das ein Indiz dafür, das alles im grünen Bereich ist.
Hier das Skript, das ich temperatur-server-cpu.py genannt habe: Getestet wurde es auf Raspbian Buster mit Asterisk 16.2.1
#!/usr/bin/env python # -*- coding: utf-8 -*- # Sagt die aktuelle Temperatur in Grad Celsius der # der CPU des Asterisk-Servers gerundet auf Englisch auf. # 14:31 Mittwoch, 15. März 2023 # temperatur-server-cpu.py von Volker Lange-Janson SM5ZBS für Python 2.7 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) result = subprocess.check_output(["cat", "/sys/class/thermal/thermal_zone0/temp"]) cpu_temp = round(int(result) / 1000.0) sayit("/usr/share/asterisk/sounds/eigene/statusmeldungen/temp-in-celsius") sys.stdout.flush() time_duration = 1 time.sleep(time_duration) saynumber(cpu_temp); sys.stdout.flush() time_duration = 1 time.sleep(time_duration) sayit("/usr/share/asterisk/sounds/eigene/statusmeldungen/temp-in-celsius") sys.stdout.flush() time_duration = 1 time.sleep(time_duration) saynumber(cpu_temp); 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 349 # # exten => 349,1,Noop(Anruf erfolgt von: "${CALLERID(name)}" <${CALLERID(num)}>) # same => n,Answer() # same => n,AGI(temperatur-server-cpu.py) # same => n,Hangup()
Wir brauchen noch eine zusätzliches Soundfile für die Ansage: Sie ist in /usr/share/asterisk/sounds/eigene/statusmeldungen/ zu kopieren
Zum Download des Soundfiles: Soundfiles-temp-in-celsius.zip. Es ist die WAV-Datei zu verwenden. Nicht die MP3 nehmen. So erstellt man die Audiofiles:
Erzeugen von Telefon-Ansagen in verschiedenen Sprachen – 3.11.2022: Für einen Anrufbeantworter oder meinen Asterisk-Server benötige ich Sprach-Ansagen in verschiedenen Sprachen. Die weiblichen oder männlichen Stimmen sollten professionell und deutlich klingen. Wie mache ich das für private Zwecke ohne einen Cent dafür zu zahlen? Und alles soll möglichst online erstellt werden können. – weiter – |
Falls jemand die temperatur-server-cpu.py von Windows nach Linux kopiert, ist sie noch für Linux geeignet und ausführbar zu machen. Das geschieht mit folgenden Befehlen, die einmalig auszuführen sind:
sudo dos2unix /usr/share/asterisk/agi-bin/temperatur-server-cpu.py sudo chmod 777 /usr/share/asterisk/agi-bin/temperatur-server-cpu.py sudo python /usr/share/asterisk/agi-bin/temperatur-server-cpu.py
In der extension.conf kommt folgende Wahlregel für den Aufruf des Skripts zum Einsatz:
exten => 349,1,Noop(Anruf erfolgt von: "${CALLERID(name)}" <${CALLERID(num)}>) same => n,Answer() same => n,AGI(temperatur-server-cpu.py) same => n,Hangup()
Wählt man die 349, erfährt man die aktuelle Temperatur der CPU des Asterisk-Servers.
Wie funktioniert das Skript temperatur-server-cpu.py? Das Skript kommuniziert mit Asterisk und gibt die aktuelle Temperatur der CPU des Asterisk-Servers in Grad Celsius auf Englisch aus. Die Temperatur wird durch Auslesen des Dateipfads /sys/class/thermal/thermal_zone0/temp ermittelt und dann gerundet. Das Skript wird über die AGI-Schnittstelle von Asterisk aufgerufen und verwendet die Funktionen sayit und saynumber, um die Ausgabe der Temperatur auf Englisch zu realisieren. Die Umsetzung dieser Funktionen für die AGI-Schnittstelle wird in den ersten 87 Zeilen beschrieben.
Ab Zeile 87 beginnt das eigentliche Programm. Das Skript wartet eine Sekunde, bevor es mit der Ausgabe beginnt. Dann wird die Begrüßung „hello“ auf Englisch durch die Funktion sayit ausgegeben. Danach wird die Temperatur der CPU des Servers ausgelesen und gerundet. Die Ausgabe wird durch die Funktion sayit mit der Ansage „temp-in-celsius“ eingeleitet, gefolgt von der Funktion saynumber, die die gerundete Temperatur ausgibt. Danach wird die Ausgabe wiederholt und zum Schluss wird mit der Funktion sayit die Verabschiedung „goodbye“ ausgegeben. Das Skript wird über die AGI-Schnittstelle von Asterisk aufgerufen und kann in der Datei „extensions.conf“ durch die Wahl der Erweiterung „349“ aufgerufen werden.
Diese beiden Zeilen
result = subprocess.check_output(["cat", "/sys/class/thermal/thermal_zone0/temp"]) cpu_temp = round(int(result) / 1000)
lesen die aktuelle Temperatur der CPU des Computers aus, auf dem dieses Python-Skript ausgeführt wird. Dazu wird die Ausgabe des Shell-Kommandos „cat /sys/class/thermal/thermal_zone0/temp“ als Byte-String in der Variable „result“ gespeichert. Anschließend wird dieser Byte-String in eine Ganzzahl (Integer) umgewandelt, durch 1000 geteilt und auf die nächstkleinere Ganzzahl gerundet. Das Ergebnis ist die aktuelle CPU-Temperatur in Grad Celsius, die in der Variablen „cpu_temp“ gespeichert wird.
Der Befehl für die Ausgabe der CPU-Temperatur auf Debian lautet:
cat /sys/class/thermal/thermal_zone0/temp
Der Befehl gibt die CPU-Temperatur in Tausendstel Grad Celsius (m°C) aus.
****
Erweiterte Version gibt die Temperatur in Fahrenheit und Celsius an:
#!/usr/bin/env python # -*- coding: utf-8 -*- # Sagt die aktuelle Temperatur in Grad Celsius der # der CPU des Asterisk-Servers gerundet auf Englisch auf. # 14:31 Mittwoch, 15. März 2023 # temperatur-server-cpu.py von Volker Lange-Janson SM5ZBS für Python 2.7 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) result = subprocess.check_output(["cat", "/sys/class/thermal/thermal_zone0/temp"]) cpu_temp = round(int(result) / 1000.0) fahrenheit = round(int(result) / 1000.0 * 1.8 + 32) sayit("/usr/share/asterisk/sounds/eigene/statusmeldungen/temp-in-celsius") sys.stdout.flush() time_duration = 1 time.sleep(time_duration) saynumber(cpu_temp); sys.stdout.flush() time_duration = 1 time.sleep(time_duration) sayit("/usr/share/asterisk/sounds/eigene/statusmeldungen/temp-in-fahrenheit") sys.stdout.flush() time_duration = 1 time.sleep(time_duration) saynumber(fahrenheit); 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 349 # # exten => 349,1,Noop(Anruf erfolgt von: "${CALLERID(name)}" <${CALLERID(num)}>) # same => n,Answer() # same => n,AGI(temperatur-server-cpu.py) # same => n,Hangup()
Der Kern des Programms für die Berechnung von Celsius und Fahrenheit:
result = subprocess.check_output(["cat", "/sys/class/thermal/thermal_zone0/temp"]) cpu_temp = round(int(result) / 1000.0) fahrenheit = round(int(result) / 1000.0 * 1.8 + 32)
Die beiden Soundfiles für die Ansage von Fahrenheit und Celsius:
Temperatur in Celsius:
Temperatur in Fahrenheit:
Download der WAV-Dateien für Asterisk: Soundfiles Fahrenheit Celsius.zip
Das passiert im Asterisk CLI, wenn die 1088 die 349 anruft:
-- Executing [349@janson:1] NoOp("SIP/1088-000000b1", "Anruf erfolgt von: "Volker Lange-Janson" <1088>") in new stack -- Executing [349@janson:2] Answer("SIP/1088-000000b1", "") in new stack > 0x741fc090 -- Strict RTP switching to RTP target address 192.168.1.44:10000 as source -- Executing [349@janson:3] AGI("SIP/1088-000000b1", "temperatur-server-cpu.py") in new stack -- Launched AGI Script /usr/share/asterisk/agi-bin/temperatur-server-cpu.py <SIP/1088-000000b1>AGI Tx >> agi_request: temperatur-server-cpu.py <SIP/1088-000000b1>AGI Tx >> agi_channel: SIP/1088-000000b1 <SIP/1088-000000b1>AGI Tx >> agi_language: en <SIP/1088-000000b1>AGI Tx >> agi_type: SIP <SIP/1088-000000b1>AGI Tx >> agi_uniqueid: 1678990470.382 <SIP/1088-000000b1>AGI Tx >> agi_version: 16.2.1~dfsg-1+deb10u2 <SIP/1088-000000b1>AGI Tx >> agi_callerid: 1088 <SIP/1088-000000b1>AGI Tx >> agi_calleridname: Volker Lange-Janson <SIP/1088-000000b1>AGI Tx >> agi_callingpres: 0 <SIP/1088-000000b1>AGI Tx >> agi_callingani2: 0 <SIP/1088-000000b1>AGI Tx >> agi_callington: 0 <SIP/1088-000000b1>AGI Tx >> agi_callingtns: 0 <SIP/1088-000000b1>AGI Tx >> agi_dnid: 349 <SIP/1088-000000b1>AGI Tx >> agi_rdnis: unknown <SIP/1088-000000b1>AGI Tx >> agi_context: janson <SIP/1088-000000b1>AGI Tx >> agi_extension: 349 <SIP/1088-000000b1>AGI Tx >> agi_priority: 3 <SIP/1088-000000b1>AGI Tx >> agi_enhanced: 0.0 <SIP/1088-000000b1>AGI Tx >> agi_accountcode: <SIP/1088-000000b1>AGI Tx >> agi_threadid: 1840911264 <SIP/1088-000000b1>AGI Tx >> <SIP/1088-000000b1>AGI Rx << STREAM FILE /usr/share/asterisk/sounds/en_US_f_Allison/hello "" -- <SIP/1088-000000b1> Playing '/usr/share/asterisk/sounds/en_US_f_Allison/hello.gsm' (escape_digits=) (sample_offset 0) (language 'en') <SIP/1088-000000b1>AGI Tx >> 200 result=0 endpos=6400 <SIP/1088-000000b1>AGI Rx << STREAM FILE /usr/share/asterisk/sounds/eigene/statusmeldungen/temp-in-celsius "" -- <SIP/1088-000000b1> Playing '/usr/share/asterisk/sounds/eigene/statusmeldungen/temp-in-celsius.slin' (escape_digits=) (sample_offset 0) (language 'en') > 0x741fc090 -- Strict RTP learning complete - Locking on source address 192.168.1.44:10000 <SIP/1088-000000b1>AGI Tx >> 200 result=0 endpos=36987 <SIP/1088-000000b1>AGI Rx << SAY NUMBER 40.0 "" -- <SIP/1088-000000b1> Playing 'digits/40.gsm' (language 'en') <SIP/1088-000000b1>AGI Tx >> 200 result=0 <SIP/1088-000000b1>AGI Rx << STREAM FILE /usr/share/asterisk/sounds/eigene/statusmeldungen/temp-in-fahrenheit "" -- <SIP/1088-000000b1> Playing '/usr/share/asterisk/sounds/eigene/statusmeldungen/temp-in-fahrenheit.slin' (escape_digits=) (sample_offset 0) (language 'en') <SIP/1088-000000b1>AGI Tx >> 200 result=0 endpos=36778 <SIP/1088-000000b1>AGI Rx << SAY NUMBER 103.0 "" -- <SIP/1088-000000b1> Playing 'digits/1.gsm' (language 'en') -- <SIP/1088-000000b1> Playing 'digits/hundred.gsm' (language 'en') -- <SIP/1088-000000b1> Playing 'digits/3.gsm' (language 'en') <SIP/1088-000000b1>AGI Tx >> 200 result=0 <SIP/1088-000000b1>AGI Rx << STREAM FILE /usr/share/asterisk/sounds/en_US_f_Allison/goodbye "" -- <SIP/1088-000000b1> Playing '/usr/share/asterisk/sounds/en_US_f_Allison/goodbye.gsm' (escape_digits=) (sample_offset 0) (language 'en') <SIP/1088-000000b1>AGI Tx >> 200 result=0 endpos=7520 -- <SIP/1088-000000b1>AGI Script temperatur-server-cpu.py completed, returning 0 -- Executing [349@janson:4] Hangup("SIP/1088-000000b1", "") in new stack == Spawn extension (janson, 349, 4) exited non-zero on 'SIP/1088-000000b1' raspberrypi*CLI>
******
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 – |