I'm trying to design an app that can communicate with my router programmatically using the same endpoints as the web interface (there's a demo on TP-Link's website). My router is a TP-Link TD-W8980, if that matters.
The format appears to be very difficult to decipher. Here is a request which obtains the data for the status part of my app. This can obtain a valid response from the router but I'm not sure why!
I'm especially confused by the #0,0,0,0,0,0#0,0,0,0,0,0] part of the response. It's the only part I haven't managed to work out but I think I recall reading it's to do with the stack?!?
[SYS_MODE#0,0,0,0,0,0#0,0,0,0,0,0]0,1
mode
[LAN_HOST_CFG#1,0,0,0,0,0#0,0,0,0,0,0]1,1
DNSServers
[WAN_DSL_INTF_CFG#1,0,0,0,0,0#0,0,0,0,0,0]2,8
upstreamCurrRate
downstreamCurrRate
upstreamMaxRate
downstreamMaxRate
upstreamNoiseMargin
downstreamNoiseMargin
upstreamAttenuation
downstreamAttenuation
[IGD_DEV_INFO#0,0,0,0,0,0#0,0,0,0,0,0]3,3
softwareVersion
hardwareVersion
upTime
[LAN_IP_INTF#0,0,0,0,0,0#0,0,0,0,0,0]4,2
IPInterfaceIPAddress
X_TPLINK_MACAddress
[LAN_HOST_ENTRY#0,0,0,0,0,0#0,0,0,0,0,0]5,4
leaseTimeRemaining
MACAddress
hostName
IPAddress
[WAN_PPP_CONN#0,0,0,0,0,0#0,0,0,0,0,0]6,4
enable
connectionStatus
externalIPAddress
DNSServers
If it helps, the names in capitals (e.g. SYS_MODE) is the name of the section. The number after the ] is a counter stating the section number (sections can be in any order). The final number following the , is the number of parameters that follow in this section.
There are also request types for each section. In the example above, the URL is http://192.168.1.1/cgi?1&1&1&1&5&5&5. As you can see the two main request types are 1 and 5.
Here is an example response from the server. As you can see, some of the sections can be returned more than once, which makes the first number of the six zeros increment each time.
[0,0,0,0,0,0]0
mode=DSL
[1,0,0,0,0,0]1
DNSServers=x.x.x.x,x.x.x.x
[1,0,0,0,0,0]2
upstreamCurrRate=928
downstreamCurrRate=3072
upstreamMaxRate=1068
downstreamMaxRate=3104
upstreamNoiseMargin=60
downstreamNoiseMargin=57
upstreamAttenuation=295
downstreamAttenuation=546
[0,0,0,0,0,0]3
softwareVersion=0.6.0 1.3 v000e.0 Build 131012 Rel.51720n
hardwareVersion=TD-W8980 v1 00000000
upTime=x
[1,1,0,0,0,0]4
IPInterfaceIPAddress=192.168.1.1
X_TPLINK_MACAddress=xx:xx:xx:xx:xx:xx
[1,0,0,0,0,0]5
leaseTimeRemaining=-1
MACAddress=xx:xx:xx:xx:xx:xx
hostName=X
IPAddress=192.168.1.2
[2,0,0,0,0,0]5
leaseTimeRemaining=-1
MACAddress=xx:xx:xx:xx:xx:xx
hostName=X
IPAddress=192.168.1.4
[3,0,0,0,0,0]5
leaseTimeRemaining=-1
MACAddress=xx:xx:xx:xx:xx:xx
hostName=X
IPAddress=192.168.1.11
[4,0,0,0,0,0]5
leaseTimeRemaining=-1
MACAddress=xx:xx:xx:xx:xx:xx
hostName=X
IPAddress=192.168.1.5
[1,2,1,0,0,0]6
enable=1
connectionStatus=Connected
externalIPAddress=x.x.x.x
DNSServers=x.x.x.x,x.x.x.x
[2,1,1,0,0,0]6
enable=0
connectionStatus=Unconfigured
externalIPAddress=0.0.0.0
DNSServers=0.0.0.0,0.0.0.0
[3,1,1,0,0,0]6
enable=0
connectionStatus=Unconfigured
externalIPAddress=0.0.0.0
DNSServers=0.0.0.0,0.0.0.0
[error]0
I would appreciate any explanation of this format and if it appears anywhere else on the web. I've never seen such a system before!
I'm building an Application that integrates with your Nest devices (both the thermostat and the Nest Protect, but this issue is about the thermostat).
What I'm trying to do is set my thermostat's ETA to be in x minutes (2 hours for example so 120 minutes).
This is my code that I'm executing:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
final String path = buildStructureFieldPath(structureID, Keys.STRUCTURE.ETA);
Structure.ETA eta = new Structure.ETA.Builder()
.setTripID(tripId)
.setEstimatedArrivalWindowBegin(sdf.format(estimatedArrivalBegin))
.setEstimatedArrivalWindowEnd(sdf.format(estimatedArrivalEnd))
.build();
sendRequest(path, eta.toJSON().toString(), listener);
The path is /structures/MY_STRUCTURE_ID/eta
Unfortunately that's not working. I'm always getting an error code -2 and error message: No write permission(s) for field(s): eta
And that's were it gets strange. No permission, but I did request the permission and I did an authenticate, which is successful, before launching the update call.
In the two attached screenshots you can see first my Nest Developer Account where you can find the ETA write permission and in the second you can see the logging from within my app (using the NestAPI as can be found on GitHub, just added the ETA feature myself).
Anyone have any idea on how to solve this issue?
Can you print out the exact JSON blob you're sending and post it here? (the value of eta.toJSON().toString())
Best guess is that it isn't formatted exactly correctly and as such is maybe attempting to write in such a way that doesn't adhere to the api-reference.
This is the format that it needs to match:
"eta": {
"trip_id": "myTripHome1024" ,
"estimated_arrival_window_begin": "2015-10-31T22:42:59.000Z" ,
"estimated_arrival_window_end": "2015-10-31T23:59:59.000Z"
}
Single line:
{"eta":{"trip_id":"myTripHome1024","estimated_arrival_window_begin":"2015-10-31T22:42:59.000Z","estimated_arrival_window_end": "2015-10-31T23:59:59.000Z"}}
To pinpoint exactly which field may be erroneous, try sending just one change at a time for each ie: structures/ID/eta/trip_id, etc for the others.
Useful JSON Validator: http://jsonlint.com/
You could also try to send it to /structures/MY_STRUCTURE_ID.json?auth=[TOKEN] instead of /structures/MY_STRUCTURE_ID/eta.
I am opening a connection setting up a custom protocol like this:
WebSocketSubProtocol d = new WebSocketSubProtocol("MyCustomProto",WebSocketEncoding.TEXT);
mJWC.addSubProtocol(d);
mJWC.open(mURL);
But... Server side, I receive tis in the protocol string
"org.jwebsocket.json MyCustomProto"
How can I remove from the string the "org.jwebsocket.json" ?
I don't wanna do it server side...
Thanks!
I will answer to my own question.
By calling the "addSubProtocol" doesn't seem to be the right solution for couple of reasons:
if you call those 3 lines of code multiple time (if the first time the connection failed for example..) well the the protocol string would be something like
"org.jwebsocket.json MyCustomProto MyCustomProto"
It just keep adding the protocol..
So I found a turn around. Now I don't use that "addSubProtocol" but instead I defined the protocol directly when I create the socket
mJWC = new BaseTokenClient("client||"+code+"||"+name,WebSocketEncoding.TEXT);
Voila.. Now no more "org.jwebsocket.json" anymore
I need to work with a TCP socket over TLS for an app I'm working on. I've been through dozens of examples and while I have no problem getting through the handshake, I can't seem to read the input stream through any means (tried a lot, including readline(), reading to character array, etc). every time I try, the app freezes on that spot. If I debug, it never goes to the next line of code.
In an attempted solution, I decided to move over to using an SSLEngine, since that's supposed to be the Java 1.5 answer to java.nio for SSL. However, I have found one example (here: http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/samples/sslengine/SSLEngineSimpleDemo.java) which is more than a little confusing to me, and I've not been successful implementing it. When I try, the unwrap() call yields an empty buffer, where I know (from using OpenSSL on the command line) that the service in question pushes data back down the pipe.
Suggestions are welcome, I've burned way too much time on this already. Here's the relevant code:
SSLEngine engine = sslContext.createSSLEngine(uri.getHost(), uri.getPort());
engine.setUseClientMode(true);
engine.beginHandshake();
SSLSession session = engine.getSession();
int bufferMax = session.getPacketBufferSize();
int appBufferMax = session.getApplicationBufferSize() + 50;
ByteBuffer cTo = ByteBuffer.allocateDirect(bufferMax);
ByteBuffer sTo = ByteBuffer.allocateDirect(bufferMax);
ByteBuffer out = ByteBuffer.wrap(sessionId.getBytes());
ByteBuffer in = ByteBuffer.allocate(appBufferMax);
debug("sending secret");
SSLEngineResult rslt = engine.wrap(out, cTo);
debug("first result: " + rslt.toString());
sTo.flip();
rslt = engine.unwrap(sTo, in);
debug("next result" + rslt.toString());
This implementation is missing some key pieces. Namely the handshake can bounce between several states NEED_WRAP, NEED_UNWRAP, NEED_TASK to negotiate a connection. This means you cannot just call one and then the other. You will need to loop over the states until a handshake has completed.
while (handshaking) {
switch (state) {
case NEED_WRAP:
doWrap();
break;
case NEED_UNWRAP:
doUnwrap();
break;
case NEED_TASK:
doTask();
break;
}
}
A full working example of Java SSL and NIO
Now that said, you should be aware the SSLEngine on Android is broken. Google recommends using threads and blocking sockets according to that thread.
I have written something to make using SSLEngine easier. It can be used with NIO or for other use cases. Available here SSLFacade
unwrap() can yield an empty buffer if what was unwrapped was an SSL handshake message or alert, rather than application data. There's not enough information here to say more. What was the engine status afterwards?
beginHandshake does not proceed the handshake, it is just used to inform the SSLEngine that you want to perform the handshake for the next calls to wrap/unwrap.
It's useful when you want to do another handshake. For the initial one, it is not needed as the first call to wrap will initiate the handshake.
Besides, you have to check the result of the wrap and unwrap methods to know if all the data has been correctly encoded. It can happen that you have to call the methods several times to process all the data.
The following link might help:
http://onjava.com/onjava/2004/11/03/ssl-nio.html
Or this question:
SSL Handshaking Using Self-Signed Certs and SSLEngine (JSSE)
I've been trying all day to make this thing works but it's still not right yet. I've checked so many posts around here and tested so many different implementations that I'dont know where to look now...
Here is my situation, I have a small php test file (gz.php) on my server wich looks like this :
header("Content-Encoding: gzip");
print("\x1f\x8b\x08\x00\x00\x00\x00\x00");
$contents = gzcompress("Is it working?", 9);
print($contents);
This is the simplest I could do and it works fine with any web browser.
Now I have an Android activity using Jsoup that has this code :
URL url = new URL("http://myServerAdress.com/gz.php");
doc = Jsoup.parse(url, 1000);
Which cause an empty EOFException on the "Jsoup.parse" line.
I've read everywhere that Jsoup is supposed to parse gzipped content without having to do anything special, but obviously, there's something missing.
I've tried many other ways like using Jsoup.connect().get() or InpuStream, GZipInputStream and DataInpuStream. I did try the gzDeflate() and gzencode() methods from PHP as well but no luck either. I even tried not to declare the header-encoding in PHP and try to deflate the content later...but it was as clever as effective...
It has to be something "stupid" I'm missing but I just can't tell what... anybody has an idea?
(ps : I'm using Jsoup 1.7.0, so the latest one as of now)
The asker indicated in a comment that gzcompress was writing a CRC that was both incorrect and incomplete, according to information from here, the operative code being:
// Display the header of the gzip file
// Thanks ck#medienkombinat.de!
// Only display this once
echo "\x1f\x8b\x08\x00\x00\x00\x00\x00";
// Figure out the size and CRC of the original for later
$Size = strlen($contents);
$Crc = crc32($contents);
// Compress the data
$contents = gzcompress($contents, 9);
// We can't just output it here, since the CRC is messed up.
// If I try to "echo $contents" at this point, the compressed
// data is sent, but not completely. There are four bytes at
// the end that are a CRC. Three are sent. The last one is
// left in limbo. Also, if we "echo $contents", then the next
// byte we echo will not be sent to the client. I am not sure
// if this is a bug in 4.0.2 or not, but the best way to avoid
// this is to put the correct CRC at the end of the compressed
// data. (The one generated by gzcompress looks WAY wrong.)
// This will stop Opera from crashing, gunzip will work, and
// other browsers won't keep loading indefinately.
//
// Strip off the old CRC (it's there, but it won't be displayed
// all the way -- very odd)
$contents = substr($contents, 0, strlen($contents) - 4);
// Show only the compressed data
echo $contents;
// Output the CRC, then the size of the original
gzip_PrintFourChars($Crc);
gzip_PrintFourChars($Size);
Jonathan Hedley commented, "jsoup just uses a normal Java GZIPInputStream to parse the gzip, so you'd hit that issue with any Java program." The EOFException is presumably due to the incomplete CRC.