Nachdem ich mich auch regelmäßig ärgere wenn eins der FritzBox Scripte nach einen FW-Update nicht mehr läuft, habe ich versucht einen Weg zu finden, welcher ‚vielleicht‘ etwas weniger anfällig für Firmware , Weboberfläche, Lua-Script Änderungen seitens des Herstellers ist.
Kurzer Rückblick:
Auf die Idee bin ich durch Paresy’s Script gekommen :http://www.ip-symcon.de/forum/threads/11741-Downstream-Upstream-Geschwindigkeit-der-FritzBox-auslesen
Hier wird ein SOAP-Request zur Box gesendet und die Rückmeldung ausgewertet.
Inzwischen geht das ganze recht komfortable.
Einfache Abfrage
$client = new SoapClient(
null,
array(
'location' => "http://fritz.box:49000/igdupnp/control/WANCommonIFC1",
'uri' => "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
'noroot' => True
)
);
$status = $client->GetAddonInfos();
So erhält hat man ein assoziertes Array (hier $status) welches u.a. „NewByteSendRate“ & „NewByteReceiveRate“ enthält.
Es gibt auch Dienste (das ist im Beispiel unter ‚uri‘ zu finden) welche eine Authentifizierung benötigen.
Hier ergänzt man das übergebende Array des SoapClient um die Felder ‚login‘ = Benutzername und ‚password‘ = passwort.
Nun steht es jedem natürlich frei alle Werte für seine Box manuell in x Scripte regelmässig abzufragen.
Dabei gibt es aber zwei Probleme, das ermitteln der möglichen Dienste und Funktionen (oben z.B. GetAddonInfos() ) und dass es auf den zig Modellen der Fritten nicht jeden Dienst geben muss.
Test-Script
Für das ermitteln der vorhandenen Dienste und Funktionen habe ich ein Testscript geschrieben, welche das Alles automatisch ermittelt, ausließt und in IPS als Struktur abbildet.
Warnung
Einsatz auf eigene Gefahr.
Das Script ist rein zum Auslesen /Prüfen aller Dienste gedacht.
Es sollte niemals automatisch zyklisch ausgeführt werden, da es einfach zu viel Last auf die FritzBox bringt und locker 1-3 Minuten Laufzeit hat.
Es erhebt keinen Anspruch darauf Fehlerfrei zu sein. Wenn also eure Box anfängt zu fliegen, reden oder einfach die Werkseinstellungen lädt (und ja das geht wirklich über SOAP !) macht mich dafür nicht verantwortlich.
Bevor der Code folgt, noch ein paar Infos wie Ihr eure Dienste selbst ermitteln könnt, bzw. wie es auch das Script macht:
[ol]
[li]Es werden die XML Dateien mit xxxxxdesc.xml geladen und nach den Diensten (SCPD, URI und Location) durchsucht[/li][li]Die dazugehörige SCPD-Datei wird geladen und nach allen Funktionen für diesen Dienst durchsucht.[/li][li]Die Abschnitte der Funktionen enthalten Informationen darüber welche Daten als Parameter übergeben werden müssen (direction=in) oder welche Daten als Ergebnis zurück geliefert werden (direction=out).[/li][li]Das Testscript nimmt nur Funktionen ohne Parameter (also alles was mindesten ein out-Parameter und keinen in-Parameter hat) und führt einen SOAPRequest durch. Das Ergebnis wird angezeigt und im Baum abgebildet.[/li][/ol]
Um also jetzt zu Fuss einen SOAPRequest zusammen zu bauen, müßt Ihr euch die Dateien von Punkt 1 und 2 ansehen (im Browser als http://:49000/ aufrufen) und dann anhand der Parameter der 2.Datei ‚raten‘ welche Funktion ihr haben wollt.
Im zweiten Post folgen dann noch ein paar Beispiele für direkte Abfragen und ein paar Funktionen, welche euch das Leben etwas einfach machen sollten (um nicht alles statisch im PHP-Code zu codieren).
Im dritten Post gibt es dann noch Beispiele wie man Daten per SOAP schreibt (z.B. Nachtschaltung, Reboot auslösen). Aber das wird erst die Tage etwas, leide gerade etwas unter Zeitmangel…
Diese beiden Post werde ich gerne immer mal wieder mit Beispielen ergänzen und auch versuchen etwa in Schuss zu halten. Dazu wären natürlich Rückmeldungen von Euch nötig (mit Angabe der Typenbezeichnung und Firmware Eurer Box) welche Abfragen funktionieren.
<?
set_time_limit(240);
$fbroot ='http://fritz.box:49000';
$fb_user='asdf@asdf.asdf';
$fb_password='secret';
$descXMLs = array(
"tr64desc.xml",
"igddesc.xml",
//"MediaServerDevDesc.xml", //gibt es bei mir nicht
//"onlinestoredesc.xml", //gibt es bei mir nicht
//"fboxdesc.xml", //gibt es bei mir nicht
"usbdesc.xml"
);
// ab hier nichts ändern
foreach ($descXMLs as $descXML)
{
$xml = @simplexml_load_file($fbroot.'/'.$descXML);
if ($xml === false)
{
echo "Not found:".$descXML.PHP_EOL;
continue;
}
$xml->registerXPathNamespace('fritzbox', $xml->getNameSpaces(false)[""]);
$xmlservices = $xml->xpath('//fritzbox:service');
foreach ($xmlservices as $service)
{
$serviceIdent=str_replace(array(".",":","-","_"),array("","","",""),(string)$service->serviceId);
if (!ctype_alnum($serviceIdent))
{
echo "ERROR: Konnte Dienst nicht hinzufügen. Name kann nicht als Ident verwendet werden:".(string)$service->serviceId.PHP_EOL;
continue;
}
$services[$serviceIdent]['descXML'] = $descXML;
$services[$serviceIdent]['uri'] = (string)$service->serviceType;
$services[$serviceIdent]['location'] =$fbroot.(string)$service->controlURL;
$services[$serviceIdent]['SCPDURL'] =trim((string)$service->SCPDURL,"/");
}
}
$parent = IPS_GetParent($_IPS['SELF']);
foreach ($services as $serviceIdent=>$data)
{
$StateVariables = array();
$service_id = @IPS_GetObjectIDByIdent($serviceIdent,$parent);
if ($service_id===false)
{
$service_id = IPS_CreateCategory();
IPS_SetIdent($service_id, $serviceIdent);
IPS_SetParent($service_id, $parent);
IPS_SetName($service_id,$data['SCPDURL']);
IPS_SetInfo($service_id,$data['uri']);
}
$xmlDesc = @simplexml_load_file($fbroot.'/'.$data['SCPDURL']);
if ($xmlDesc === false)
{
echo "Not found:".$data['SCPDURL'].PHP_EOL;
continue;
}
$xmlDesc->registerXPathNamespace('fritzbox', $xmlDesc->getNameSpaces(false)[""]);
$xmlActionList = $xmlDesc->xpath("//fritzbox:action[fritzbox:argumentList/fritzbox:argument/fritzbox:direction ='out']");
$xmlStateVariable = $xmlDesc->xpath('//fritzbox:stateVariable');
foreach ($xmlStateVariable as $StateVariable)
{
$StateVariables[(string)$StateVariable->name] = (string)$StateVariable->dataType;
}
foreach ($xmlActionList as $ActionList)
{
foreach ($ActionList->argumentList->argument as $Argument)
{
if ((string)$Argument->direction == "in") continue 2;
$relatedStateVariable[(string)$Argument->name]=(string)$Argument->relatedStateVariable;
}
$client = new SoapClient(null,
array(
'location' => $data["location"],
'uri' => $data["uri"],
'noroot' => True,
'login' => $fb_user,
'password' => $fb_password,
'trace'=>TRUE,
'exceptions'=>false
)
);
$status = $client->{(string)$ActionList->name}();
if (is_soap_fault($status))
{
echo "--------------ERROR START----------------".PHP_EOL;
echo " descXML: ".$data['descXML'].PHP_EOL;
echo " SCPDURL: ".$data['SCPDURL'].PHP_EOL;
echo " Location: ".$data["location"].PHP_EOL;
echo " URI: ".$data["uri"].PHP_EOL;
echo " Action: ".(string)$ActionList->name.PHP_EOL;
echo "--------------ERROR END------------------".PHP_EOL.PHP_EOL;;
continue;
} else {
echo " descXML: ".$data['descXML'].PHP_EOL;
echo " SCPDURL: ".$data['SCPDURL'].PHP_EOL;
echo " Location: ".$data["location"].PHP_EOL;
echo " URI: ".$data["uri"].PHP_EOL;
echo " Action: ".(string)$ActionList->name.PHP_EOL;
print_r($status);
echo PHP_EOL.PHP_EOL;
}
$actionIdent=str_replace(array(".",":","-","_"),array("","","",""),(string)$ActionList->name);
if (!ctype_alnum($actionIdent))
{
echo "ERROR: Konnte Action nicht hinzufügen. Name kann nicht als Ident verwendet werden:".(string)$ActionList->name.PHP_EOL;
continue;
}
$action_id = @ IPS_GetObjectIDByIdent($actionIdent,$service_id);
if ($action_id===false)
{
$action_id = IPS_CreateInstance("{485D0419-BE97-4548-AA9C-C083EB82E61E}");
IPS_SetIdent($action_id, $actionIdent);
IPS_SetParent($action_id, $service_id);
IPS_SetName($action_id,(string)$ActionList->name);
}
if (count($ActionList->argumentList->argument) == 1)
{
UpdateIPSvar($action_id,(string)$ActionList->argumentList->argument->relatedStateVariable,$status,$StateVariables[(string)$ActionList->argumentList->argument->relatedStateVariable]);
}
else
{
foreach ($status as $key=>$value)
{
UpdateIPSvar($action_id,$relatedStateVariable[$key],$value,$StateVariables[$relatedStateVariable[$key]]);
}
}
}
}
function UpdateIPSvar($parent,$ident,$value,$type)
{
$ident=str_replace(array(".",":","-","_"),array("","","",""),$ident);
if (!ctype_alnum($ident))
{
echo "ERROR: Konnte Variable nicht hinzufügen. Name kann nicht als Ident verwendet werden:".$ident.PHP_EOL;
return;
}
switch ($type)
{
case "i1":
case "i2":
case "i4":
case "ui1":
case "ui2":
case "ui4":
$ips_type=1;
break;
case "boolean":
$ips_type=0;
break;
case "uuid":
case "dateTime":
case "string":
$ips_type=3;
break;
default:
echo "Unbekannt:".$type.PHP_EOL;
return;
break;
}
$var_id = @IPS_GetObjectIDByIdent($ident,$parent);
if ($var_id === false)
{
$var_id = IPS_CreateVariable($ips_type);
IPS_SetName($var_id,$ident);
IPS_SetIdent($var_id,$ident);
IPS_SetParent($var_id,$parent);
}
switch ($ips_type)
{
case 0:
if (GetValueBoolean($var_id) <> (bool)$value)
{
SetValueBoolean($var_id,(bool)$value);
}
break;
case 1:
if (GetValueInteger($var_id) <> (int)$value)
{
SetValueInteger($var_id,(int)$value);
}
break;
case 2:
if (GetValueFloat($var_id) <> round((float)$value,2))
{
SetValueFloat($var_id,round((float)$value,2));
}
break;
case 3:
if (GetValueString($var_id) <> $value)
{
SetValueString($var_id,$value);
}
break;
}
}
?>
Ihr erhaltet einen Ausgabe die in für jede Aktion folgendes enthält:
SCPDURL: deviceinfoSCPD.xml
Location: http://fritz.box:49000/upnp/control/deviceinfo
URI: urn:dslforum-org:service:DeviceInfo:1
Action: GetInfo
Array
(
[NewManufacturerName] => AVM
[NewManufacturerOUI] => 00040E
[NewModelName] => FRITZ!Box 6360 Cable (kdg)
[NewDescription] => FRITZ!Box 6360 Cable (kdg) 85.06.05
[NewProductClass] => FRITZ!Box
.....
Die ersten 4 Zeilen zeigen welcher Dienst mit welcher Funktion aufgerufen wurde.
Ab der 5. Zeile kommen die von der Box gemeldeten Werte.
Sollte die Abfrage fehlgeschlagen sein sieht das so aus:
--------------ERROR START----------------
SCPDURL: deviceconfigSCPD.xml
Location: http://fritz.box:49000/upnp/control/deviceconfig
URI: urn:dslforum-org:service:DeviceConfig:1
Action: ConfigurationFinished
--------------ERROR END------------------
Außerdem können erfolgreichen Abfragen aus dem Objektbaum rekonstruiert werden.
Dazu jetzt mal ein Bild
Michael