[Problem] Fritzbox - Beispiel für SOAP Authentifizierung gesucht

JED

Neuer User
Mitglied seit
14 Feb 2016
Beiträge
3
Punkte für Reaktionen
0
Punkte
1
Hallo zusammen,
in den letzten Tagen habe ich Stunden damit verbracht die SOAP-Dokumentation der Fritzbox, z.B. 7390,
zu studierenden und das Internet nach funktionierenden Beispielen zu durchsuchen wie eine erfolgreiche
Authentifizierung mittels Usernamen und Kennwort zu erfolgen hat - leider ohne Erfolg.

Ziel ist es final ein Shell-Skript zu erstellen, welches es mittels curl/wget erlaubt, ein Portforwarding
einzurichten. Bis dato scheitert es jedoch bereits an der initialen Digest-Authentifizierung, da die bisherigen
Beispiele alle scheinbar nicht funktionieren wollen.
Meine Hoffnung ist, hier jemanden zu finden der mir ein lauffähiges Beispiel zur Verfügung stellen kann.
Selbst ein lauffähiges PHP-Skript sollte helfen, da ich dann gegebenenfalls den Netzwerkverkehr mitschneiden
und analysieren kann.

Vielen Dank vorab!

Gruß Jürgen
 
Ja, dieses Beispiel kenne ich. Jedoch greift man hier über die normale Webschnittstelle
auf die Fritzbox zu, nicht jedoch über die SOAP-Schnittstelle die auf Port 49000/tcp hört.
 
Danke, dies war die Initiale Zündung die ich noch benötigte. Mit Hilfe der gelieferten Informationen
konnte ich nun erstmalig erfolgreich eine Fritzbox-Konfigurationsdatei abfragen. Da ich bei meinen
Recherchen vielfach mit der Fragestellung der korrekten Authentifizierung konfrontiert wurde, poste
ich hier einmal mein Testskript. Ich hoffe es bringt auch anderen Suchenden die Erleuchtung :grin:

#!/bin/sh
fb_name='fritz.box'
fb_user='user'
fb_pass='password'
fb_soap_port='49000'

fb_soap_secure_port=`curl -X POST "http://${fb_name}:${fb_soap_port}/upnp/control/deviceinfo" \
-H 'Content-Type: text/xml; charset="utf-8"' \
-H 'SoapAction: urn:dslforum-org:service:DeviceInfo:1#GetSecurityPort' \
-d '<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:GetSecurityPort xmlns:u="urn:dslforum-org:service:DeviceInfo:1">
</u:GetSecurityPort>
</s:Body>
</s:Envelope>' | \
grep 'NewSecurityPort' | sed 's#^.*<NewSecurityPort>\(.*\)<.*$#\1#'`

fb_config_file=`curl --anyauth -u "${fb_user}:${fb_pass}" --insecure \
-X POST "https://${fb_name}:${fb_soap_secure_port}/upnp/control/deviceconfig" \
-H 'Content-Type: text/xml; charset="utf-8"' \
-H 'SoapAction: urn:dslforum-org:service:DeviceConfig:1#X_AVM-DE_GetConfigFile' \
-d "<?xml version=\"1.0\"?>
<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"
s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">
<s:Body>
<u:X_AVM-DE_GetConfigFile xmlns:u=\"urn:dslforum-org:service:DeviceConfig:1\">
<NewX_AVM-DE_Password>${fb_pass}</NewX_AVM-DE_Password>
</u:X_AVM-DE_GetConfigFile>
</s:Body>
</s:Envelope>" | \
grep 'NewX_AVM-DE_ConfigFileUrl' | sed 's#^.*<NewX_AVM-DE_ConfigFileUrl>\(.*\)<.*$#\1#'`

wget -O /shares/tmp/soap/fb-config.txt ${fb_config_file}
 
Ich weiß, der Beitrag ist schon etwas älter, aber ich habe mal dein Shellskript zum Anlass genommen das in python3 zu portieren und habe dabei noch eine Abfrage der einzelnen Werte ergänzt. Ich bin jetzt nicht der python3 Profi, also verzeiht mir bitte den einen oder anderen Schönheitsfehler im Skript. Hintergrund ist, dass ich einfach die Temperaturen in eine InfluxDB schaufeln wollte und mir dafür die gängigen Projekte viel zu viele Funktionen aufweisen. Über das AHA-HTTP-Interface wollte ich jetzt nicht gehen, da mir das zu kompliziert war und ein abgespeckten Benutzer mit nur "Smart Home" Zugriffsrechten beim AHA-HTTP-Interface irgendwie nicht funktioniert. Die DECT Geräte liefern hier alle 15 Minuten einen Wert in 0,5K Schritten und ergänzen so ein paar andere Sensoren, die ich bereits in Verwendung habe. Den Abschnitt für die InfluxDB habe ich jetzt mal weg gelassen.

Python:
#!/usr/bin/env python3

# ==========================================================================================================================

import requests, warnings, tempfile               

from requests.auth import HTTPDigestAuth

# URL der Fritzbox
fritzbox_url = "https://192.168.178.1"

# Benutzername
Username = "User"

# Passwort
Password = "123456"

# Geräteliste anlegen
# 09995 0123456 --> DECT 301
# 08761 0123456 --> DECT 200
# 11630 0123456 --> DECT 200
# 13077 0123456 --> DECT 500, gibt hier keine Werte aus
device_list = ['09995 0123456','08761 0123456','11630 0123456','13077 0123456']

# Linie ziehen
print ("------------------------------------")

# Geräte aus der Geräteliste abfragen
# ==========================================================================================================================
for i, device_ain in enumerate(device_list):

        # Header erstellen
        request_headers = {'Content-Type': 'text/xml; charset="utf-8"', 'SoapAction': 'urn:dslforum-org:service:X_AVM-DE_Homeauto:1#GetSpecificDeviceInfos'}

        # Daten erstellen für POST
        request_data = '<?xml version=\"1.0\"?> \
        <s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" \
        s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"> \
        <s:Body> \
        <u:GetSpecificDeviceInfos xmlns:u=\"urn:dslforum-org:service:X_AVM-DE_Homeauto:1\"> \
        <NewAIN>00000 0000000</NewAIN> \
        </u:GetSpecificDeviceInfos> \
        </s:Body> \
        </s:Envelope>'

        # AIN durch die aus der Liste austauschen
        request_data = request_data.replace("00000 0000000", device_ain)

        # selbstsigniertes Zertifikat der Fritzbox verursacht eine Warnung, daher Fehler unterdrücken
        warnings.simplefilter('ignore')

        # Anfrage starten
        request_result = requests.post(fritzbox_url + ":49443/upnp/control/x_homeauto", auth=HTTPDigestAuth(Username, Password), headers=request_headers, data=request_data, verify=False)

        # Warnungen wieder erlauben
        warnings.resetwarnings()

        # Zum Debuggen, alles Ausgeben
        #print (request_result.text)

        # Checken, ob es Fehler gibt und wenn ja, welche. Wenn nicht, dann weiter...
        if "s:Fault" in request_result.text:
                # Datenanfrage in einzelne Variablen packen
                fault_string = str((request_result.text).split("<faultstring>")[1].split("</faultstring")[0])
                fault_error_code = int((request_result.text).split("<errorCode>")[1].split("</errorCode>")[0])
                fault_error_description = str((request_result.text).split("<errorDescription>")[1].split("</errorDescription>")[0])
              
                print ("AIN:                  ",device_ain)
                print ("Fault string:         ",fault_string)
                print ("Error code:           ",fault_error_code)
                print ("Error description:    ",fault_error_description)
                print ("Possible reason:       device_list contains wrong AIN, device is not connected due to an empty battery!")

        else:
                # Datenanfrage in einzelne Variablen packen
                device_id = int((request_result.text).split("<NewDeviceId>")[1].split("</NewDeviceId>")[0])
                device_function_bitmask = int((request_result.text).split("<NewFunctionBitMask>")[1].split("</NewFunctionBitMask>")[0])
                device_firmware = float((request_result.text).split("<NewFirmwareVersion>")[1].split("</NewFirmwareVersion>")[0])
                device_manufacturer = str((request_result.text).split("<NewManufacturer>")[1].split("</NewManufacturer>")[0])
                device_product_name = str((request_result.text).split("<NewProductName>")[1].split("</NewProductName>")[0])
                device_device_name = str((request_result.text).split("<NewDeviceName>")[1].split("</NewDeviceName>")[0])
                device_present = str((request_result.text).split("<NewPresent>")[1].split("</NewPresent>")[0])
              
                # Funktionen herausfinden
                device_multimeter_enabled = str((request_result.text).split("<NewMultimeterIsEnabled>")[1].split("</NewMultimeterIsEnabled>")[0])
                device_temperature_enabled = str((request_result.text).split("<NewTemperatureIsEnabled>")[1].split("</NewTemperatureIsEnabled>")[0])
                device_switch_enabled = str((request_result.text).split("<NewSwitchIsEnabled>")[1].split("</NewSwitchIsEnabled>")[0])
                device_hkr_enabled = str((request_result.text).split("<NewHkrIsEnabled>")[1].split("</NewHkrIsEnabled>")[0])
              

                # Ausgabe der einzelnen Variablen
                print ("AIN:                  ",device_ain)
                print ("ID:                   ",device_id)
                print ("Function bitmask:     ",device_function_bitmask)
                print ("Firmware version:     ",device_firmware)
                print ("Manufacturer:         ",device_manufacturer)
                print ("Product name:         ",device_product_name)
                print ("Device name:          ",device_device_name)
                print ("Connection:           ",device_present)

                if device_multimeter_enabled == 'ENABLED':
                        device_multimeter_valid = str((request_result.text).split("<NewMultimeterIsValid>")[1].split("</NewMultimeterIsValid>")[0])
                        device_multimeter_power = float((request_result.text).split("<NewMultimeterPower>")[1].split("</NewMultimeterPower>")[0])/100
                        device_multimeter_energy = float((request_result.text).split("<NewMultimeterEnergy>")[1].split("</NewMultimeterEnergy>")[0])/1000
                      
                        print ("Multimeter function:  ",device_multimeter_enabled)
                        print ("Multimeter values:    ",device_multimeter_valid)
                        print ("Multimeter power:     ",device_multimeter_power,"W")
                        print ("Multimeter energy:    ",device_multimeter_energy,"kWh")

                if device_temperature_enabled == 'ENABLED':
                        device_temperature_valid = str((request_result.text).split("<NewTemperatureIsValid>")[1].split("</NewTemperatureIsValid>")[0])
                        device_temperature = float((request_result.text).split("<NewTemperatureCelsius>")[1].split("</NewTemperatureCelsius>")[0])/10
                        device_temperature_offset = float((request_result.text).split("<NewTemperatureOffset>")[1].split("</NewTemperatureOffset>")[0])/10
                        device_temperature_set = float((request_result.text).split("<NewHkrSetTemperature>")[1].split("</NewHkrSetTemperature>")[0])/10
                        device_temperature_is = float((request_result.text).split("<NewHkrIsTemperature>")[1].split("</NewHkrIsTemperature>")[0])/10
                                              
                        print ("Temperature function: ",device_temperature_enabled)
                        print ("Temperature values:   ",device_temperature_valid)
                        print ("Temperature w. offs:  ",device_temperature,"°C")
                        print ("Temperature offset:   ",device_temperature_offset,"°C")
                        print ("Temperature set:      ",device_temperature_set,"°C")
                        print ("Temperature w/o offs: ",device_temperature_is,"°C")

                if device_switch_enabled == 'ENABLED':
                        device_switch_valid = str((request_result.text).split("<NewSwitchIsValid>")[1].split("</NewSwitchIsValid>")[0])
                        device_switch_state = str((request_result.text).split("<NewSwitchState>")[1].split("</NewSwitchState>")[0])
                        device_switch_mode = str((request_result.text).split("<NewSwitchMode>")[1].split("</NewSwitchMode>")[0])
                        device_switch_lock = int((request_result.text).split("<NewSwitchLock>")[1].split("</NewSwitchLock>")[0])
                      
                        print ("Switch function:      ",device_switch_enabled)
                        print ("Switch values:        ",device_switch_valid)
                        print ("Switch state:         ",device_switch_state)
                        print ("Switch mode:          ",device_switch_mode)
                        print ("Switch lock:          ",device_switch_lock)

                if device_hkr_enabled == 'ENABLED':
                        device_hkr_valid = str((request_result.text).split("<NewHkrIsValid>")[1].split("</NewHkrIsValid>")[0])
                        device_hkr_temp_is = float((request_result.text).split("<NewHkrIsTemperature>")[1].split("</NewHkrIsTemperature>")[0])/10
                        device_hkr_vent_set = str((request_result.text).split("<NewHkrSetVentilStatus>")[1].split("</NewHkrSetVentilStatus>")[0])
                        device_hkr_temp_set = float((request_result.text).split("<NewHkrSetTemperature>")[1].split("</NewHkrSetTemperature>")[0])/10
                        device_hkr_vent_red = str((request_result.text).split("<NewHkrReduceVentilStatus>")[1].split("</NewHkrReduceVentilStatus>")[0])
                        device_hkr_temp_red = float((request_result.text).split("<NewHkrReduceTemperature>")[1].split("</NewHkrReduceTemperature>")[0])/10
                        device_hkr_vent_com = str((request_result.text).split("<NewHkrComfortVentilStatus>")[1].split("</NewHkrComfortVentilStatus>")[0])
                        device_hkr_temp_com = float((request_result.text).split("<NewHkrComfortTemperature>")[1].split("</NewHkrComfortTemperature>")[0])/10
              
                        print ("Valve function:       ",device_hkr_enabled)
                        print ("Valve values:         ",device_hkr_valid)
                        print ("Valve temperature:    ",device_hkr_temp_is,"°C")
                        print ("Valve state:          ",device_hkr_vent_set)
                        print ("Valve temp set:       ",device_hkr_temp_set,"°C")
                        print ("Valve reduced state:  ",device_hkr_vent_red)
                        print ("Valve reduced temp:   ",device_hkr_temp_red,"°C")
                        print ("Valve comfort state:  ",device_hkr_vent_com)
                        print ("Valve comfort temp:   ",device_hkr_temp_com,"°C")

        print ("------------------------------------")


#Ende
# ==========================================================================================================================

Grund für Bearbeitung: verbessertes Python Script eingefügt
 
Zuletzt bearbeitet:
@Odium

Wäre PHP eine Alternative, wenn Du kein Python profi bist?
Falls ja, dann hilft Dir evtl. meine SOAP-Library weiter. Zumindest musst Du Dich da nicht mehr um das Thema Authentifizierung kümmern.
Das Auslesen von Temperatur-Werten aus DECT Geräten habe ich darin (noch) nicht auscodiert - aber: wenn es dafür eine SOAP-Function gibt (wovon ich jetzt aufgrund deines Beitrags ausgehe), dann existiert schon mal die Rumpf-Funktion.
Diese Function dann final auszucodieren ist kein Hexenwerk. Was dann noch fehlt ist nur noch dein Teil für den Zugriff auf die DB.

Grüße

Black Senator
 
Hallo
ich bin noch ein Neuling mit der Soap Schnittstelle. Was ich verstanden habe ist das man per PHP Sachen aus der Fritzbox auslesen kann. Ich würde gerne eine Device List mit jedem Device, jeder Mac Adresse, und den der Macadresse zugeordneten IP Adressen.
Geht das damit? Am liebest würde ich es dann mit dem Pihole oder adguard syncen. Auch eine Benutzerbezogene Zugangskontrolle wäre gut.
Wie kann man mit SOAP einfach starten. Welchen Server habt ihr genommen um Soap Abfragen zu machen oder kann man das vom Desktop aus?
Gruß Mima
 
@mima1982

Easy to go Repository: fritzsoap
Alles drin einfach und leicht! Wenn AVM eine passende Schnittstelle (Action) für deine Wünsche anbietet, dann kannst Du sie damit ohne großes Gewürge erreichen...

Beste Grüße

Black Senator
 
  • Like
Reaktionen: Kruemelino

Zurzeit aktive Besucher

Statistik des Forums

Themen
246,158
Beiträge
2,247,073
Mitglieder
373,677
Neuestes Mitglied
MK34
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.