-
Notifications
You must be signed in to change notification settings - Fork 21
How to create your own API call
- You know your username/ID and password
- You know the IP address of the API
- You have established a connection to your API and the library object is called connection.
- you know what XML and XML-tags are
**Note: ** it helps for this section to understand how the TR 064 protocol works, but is not strictly required.
High-level protocol description: The TR-064 interface offers services, which consist of groups of functions (actions). Calling an action is done by HTTP POSTing some XML to the URL associated with the service. Sometimes this requires authentication, which is handled in the background by the library.
Name | Explanation | Where to find? |
---|---|---|
tr64descr | An XML document with information on and all the services provided by the TR-064 interface. | For Fritz!Box typically at https://192.168.178.1:49443/tr64desc.xml and http://192.168.178.1:49000/tr64desc.xml (unless you assigned a different IP). Depending on the router at different places. Check the documentation for the official auto-discovery process. |
ServiceType (or simply Service) | A service is a collection of functions, generally grouped by some functionality. E.g. there is a service around WiFi that allows to get stats on it, but also control the interface. | In the tr64descr you find a list of all of these. Their name can be found in the tag `serviceType`. |
Action (or function) | A functionality provided by a service | At the SCPDURL, see below. The name of the action can be found in the first name-tag in the actions listed in that XML. |
SCPDURL | The sub-URL to GET a description of all the functions the service has. | SCPDURL-tag in the tr64descr |
controlURL | The sub-URL to POST calls to for a specific service | controlURL-tag in the tr64descr |
Argument (or parameter) | Each action has a list of arguments, that can be set/send (direction = in) or requested/received (direction = out) | At the SCPDURL. The argument names can be found in the name-tags inside the argumentList. |
Nonce | Unique identifier given by the API. With this, the realm, and your user data you generate an authorization token for restricted services and actions. | Returned by any request that requires authentication. |
Realm | Static identifier given by the API. Required to generate an authorization token for restricted services and actions. | Returned by any request that requires authentication. |
Username/ID and password | User account for the TR-064 interface (e.g. Fritz!Box user) | Set in the user interface of the TR-064 device. More information. |
Auth code | Token to authenticate yourself with the API. | Generated using nonce, realm, username, and password. See more below. |
Start at the tr64desc
(see table above) to find an overview over the services available on your TR-064 API. Most browsers should display a XML-structure. Then go to the service-element containing the serviceType-tag with a content that sound like what you want. I.e. 'urn:dslforum-org:service:WANDSLInterfaceConfig:1'. Next, check the value of the associated SCPDURL-tag. I.e. '/wandslifconfigSCPD.xml'.

Head over to the SCPDURL of the service you are interested in (e.g. https://192.168.178.1:49443/wandslifconfigSCPD.xml or http://192.168.178.1:49000/wandslifconfigSCPD.xml). You'll see what actions are available on this service.

You see for instance, that in this case there is a service called 'GetInfo', so I know I could call this service with
connection.action("urn:dslforum-org:service:WANDSLInterfaceConfig:1", "GetInfo");
Now, just calling that action will probably not do any good. I want to get certain information (arguments) from that service or pass some information (arguments) to it. For requesting arguments, this looks like this:
// Direction = out
String req[][2] = {{"argumentName1",""},{"argumentName2", ""},...};
where the ArgumentName is one of the name-elements inside one of the argument-elements inside the argumentList-element of the action that I chose above (and where the direction
is 'out', so for instance NewStatus
). The empty string behind the argumentName will be filled with the value of the associated variable when calling the action
command of this library.
Some action also take input parameters (like when you want to change the password of your WIFI), which then would be marked by a direction
tag with the value 'in'. This is then done in a similar fashion with
// Direction = in
String params[][2] = {{"argumentName1", "value1"}, {"argumentName2", "value2"}, ...};
To put together an action that would both request look like this:
String params[][2] = {{"inArgumentName1", "value1"}, {"inArgumentName2", "value2"}, ...};
nparams = 2; // Number of arguments you are passing - needs to be adapted!
String req[][2] = {{"outArgumentName1", ""}, {"outArgumentName2", ""},...};
nreq = 2; // Number of arguments you are requesting - needs to be adapted!
connection.action("serviceType", "action name", params, nparams , req, nreq);
the full command to request the status of the internet connection would then e.g. be
// In this case, we're not passing any input parameters
String params[][2] = {{}};
// and we're requesting the status-parameter
String req[][2] = {{"NewStatus",""}};
connection.action("urn:dslforum-org:service:WANDSLInterfaceConfig:1", "GetInfo", params, 0, req, 1);
// The return value can be access as:
// req[0][1]
%TODO: Add example of reading out the result
%TODO: Add example of setting a parameter
To debug/test if you request is correct, you can try to send manual requests from your browser to the API and check if the API gives the right replies. I used to do this when developing this library. You'll have to do a few steps extra that the library is doing for you. This includes looking up the controlURL
and handling the authentication manually.
Note: Please read the part about using the library above before continuing.
You first need to find the controlURL
of the service you want to call in the tr64descr
, similar to the case of using the library (see above).
Then you need to post a request to that URL. One option for that is using the built-in developer tools of any modern browser (e.g. in firefox: right-click any website and click Inspect (Q)
, then tab Network
and then the little +
in the bar=New request).
For instance, if I want to call the action GetInfo
on urn:dslforum-org:service:DeviceInfo:1
, the controlURL
on my Fritz!Box is [http://192.168.178.1:49000/upnp/control/wandslifconfig1](http://192.168.178.1:49000/upnp/control/deviceinfo)
. In that case, I POST the following XML as the body to that URL:
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Header>
</s:Header><s:Body>
<u:GetInfo xmlns:u="urn:dslforum-org:service:DeviceInfo:1"></u:GetInfo>
</s:Body>
</s:Envelope>
Replacements you'll need to do:
- in the content of the
Body
you need to insert your action name (e.g.xmlns:u="urn:dslforum-org:service:DeviceInfo:1"
) - in the content of the
Body
you need to insert your serviceType (twice - opening<u:GetInfo
and closing XML tag</u:GetInfo>
)
Additionally you need to set the header parameters: CONTENT-TYPE=text/xml
and SOAPACTION=urn:dslforum-org:service:DeviceInfo:1#GetInfo
(the latter is in the format [serviceType]#[action name]
according to what you want to call). Here is a screenshot how that looks like for me:
If the service/action does not need authentication, you'll get your response already. You can go to step 4.
If you need authentication for it, you will get a 401 Unauthorized
error. Then you need to send the exact same request again, but with an added Header
tag like this:
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Header>
<h:InitChallenge xmlns:h="http://soap-authentication.org/digest/2001/10/" s:mustUnderstand="1">
<UserID>user_ID</UserID>
</h:InitChallenge>
</s:Header>
<s:Body>
<u:GetInfo xmlns:u="urn:dslforum-org:service:WANDSLInterfaceConfig:1"></u:GetInfo>
</s:Body>
</s:Envelope>
Replacements you'll need to do:
- user_ID need to be the username of the user you want to use
- in the content of the
Header
you need to insert your action name and - in the content of the
Header
you need to insert your serviceType (twice - opening and closing XML tag)
As a response you should then get something like this:
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Header>
<h:Challenge xmlns:h="http://soap-authentication.org/digest/2001/10/" s:mustUnderstand="1">
<Status>Unauthenticated</Status>
<Nonce>597F3C98597A26CC</Nonce>
<Realm>F!Box SOAP-Auth</Realm>
</h:Challenge>
</s:Header>
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring>UPnPError</faultstring>
<detail>
<UPnPError xmlns="urn:dslforum-org:control-1-0">
<errorCode>503</errorCode>
<errorDescription>Auth. failed</errorDescription>
</UPnPError>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
Now you take the Nonce and Realm from it. If you trust me enough, you can generate the required auth codes with aypac.de/tr064_nonce.php?realm=F!Box SOAP-Auth&uid=user_ID&pw=superS3cret!&sn=597F3C98597A26CC - replace the four fields in the URL accordingly (if you don't trust me enough you can change passwords before and after using this tool or implement your own according to the documentation). Now, you need to do the exact same request again, but now with this modified Header
:
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Header>
<h:ClientAuth xmlns:h="http://soap-authentication.org/digest/2001/10/" s:mustUnderstand="1">
<Nonce>597F3C98597A26CC</Nonce>
<Auth>873bebf0f89304fb8be7dda810ece682</Auth>
<UserID>user_ID</UserID>
<Realm>F!Box SOAP-Auth</Realm>
</h:ClientAuth>
</s:Header>
<s:Body>
<u:GetInfo xmlns:u="urn:dslforum-org:service:WANDSLInterfaceConfig:1"></u:GetInfo>
</s:Body>
</s:Envelope>
Replacements you'll need to do:
- the content of the
Nonce
tag with the one from the previous request - the content of the
Auth
tag with the the auth code, e.g. generated with the link above -
user_ID
again with your username
Once you get a <Status>Authenticated</Status>
back, you should also get values back in the body.
If you either didn't need or successfully authenticated, you should get something like:
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Header>
<h:NextChallenge xmlns:h="http://soap-authentication.org/digest/2001/10/" s:mustUnderstand="1">
<Status>Authenticated</Status>
<Nonce>ECB6E4161F9EFD6F</Nonce>
<Realm>F!Box SOAP-Auth</Realm>
</h:NextChallenge>
</s:Header>
<s:Body>
<u:GetInfoResponse xmlns:u="urn:dslforum-org:service:DeviceInfo:1">
<NewManufacturerName>AVM</NewManufacturerName>
<NewManufacturerOUI>XXXXXX</NewManufacturerOUI>
<NewModelName>FRITZ!Box XXXX</NewModelName>
<NewDescription>FRITZ!Box XXXXX 272.07.30</NewDescription>
<NewProductClass>FRITZ!Box</NewProductClass>
<NewSerialNumber>XXXXXXXXXXXXX</NewSerialNumber>
<NewSoftwareVersion>272.07.30</NewSoftwareVersion>
<NewHardwareVersion>FRITZ!Box 5590</NewHardwareVersion>
<NewSpecVersion>1.0</NewSpecVersion>
<NewProvisioningCode></NewProvisioningCode>
<NewUpTime>393966</NewUpTime>
<NewDeviceLog>xx.xx.xx xx:xx:xxInternet connection renewed successfully. IP address: 192.168.xxx.xxx, DNS server: xxx.xxx.xxx.xxx and xxx.xxx.xxx.xxx, gateway:
....
</NewDeviceLog>
</u:GetInfoResponse>
</s:Body>
</s:Envelope>
Voilà, here are the values we were requesting. Note that the Header
will look different, if you didn't need the authentication.
If you get a <errorDescription>Internal Error</errorDescription>
in your reply, something is wrong with your request. E.g. you forgot to replace something in the XML or you are trying to call something that doesn't exist.
For the next request you can directly go to back to step 1 (no auth) or step 3 (with auth), using the new nonce (here D7D18F2AC4DF3F9D
) to generate a new auth code.