功能描述
最近在折腾小米米家的智能设备,其中有个特别想实现的功能:所有人离开家后,自动关闭家里的所有灯。实现这个功能的前置条件就是,能够自动登录TpLink路由器的后台,获取到当前连接WIFI的设备列表。
通过分析TpLink路由器的请求规则,发现步骤很简单,只要登录后,会有一个类似Token的字符串,后面的请求只要携带该字符串,即可正常拉取所有数据。
我的路由器型号是:TL-WDR7660
实现步骤
- 登录:TpLink路由器现在的登录很简单,账号都不用了,直接输入密码就行。但是这里的密码被TpLink登录页面的JS加密过了,所以你需要正常输入密码登录一次,然后通过浏览器的网络请求分析,把加密过的字符串复制出来。如下图所示:
- 请求数据:登录完成后,你就可以去请求自己想要的数据了。不同的数据来源于不同的请求URL,根据你的需求,自己去TpLink的管理后台,通过Chrome等浏览器去抓取请求URL和方式。这里是我抓取的WIFI设备列表,我把返回结果用JSON展示:
代码展示
核心代码只有一个Java的类,里面两个静态方法,一个登录,一个用来抓取WIFI设备列表。
其中用到了一个名为OkHttpUtil
的类,是我自己封装的OKhttp网络请求框架,后面一起放出来。
核心代码
package com.coderbbb.homeiot.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class WirelessRouterUtil {
//路由器管理后台地址。常见的为192.168.0.1或者192.168.1.1
private static final String BASE_URL = "http://192.168.1.1/";
//登录Token
private static String STOK = null;
private static final Logger logger = LoggerFactory.getLogger(WirelessRouterUtil.class);
public static void login() {
if (STOK != null) {
return;
}
String json = "{\"method\":\"do\",\"login\":{\"password\":\"4i123这里是前面抓取的,加密后的密码ewK\"}}";
String response = OkhttpUtil.postRaw(BASE_URL, "application/json; charset=UTF-8", json, null);
if (response == null) {
throw new RuntimeException("tplink login err");
}
try {
JsonNode jsonNode = ObjectMapperUtil.get().readTree(response);
if (jsonNode.has("error_code") && jsonNode.get("error_code").asInt() == 0) {
STOK = jsonNode.get("stok").asText();
logger.info("login tplink success, stok:" + STOK);
}
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 获取路由器上的WIFI设备列表
* @return
*/
public static List<JsonNode> getDeviceList() {
login();
String json = "{\"hosts_info\":{\"table\":\"host_info\",\"name\":\"cap_host_num\"},\"network\":{\"name\":\"iface_mac\"},\"hyfi\":{\"table\":[\"connected_ext\"]},\"method\":\"get\"}";
String url = BASE_URL + "stok=" + STOK + "/ds";
String response = OkhttpUtil.postRaw(url, "application/json; charset=UTF-8", json, null);
if (response == null) {
throw new RuntimeException("tplink list devices err");
}
// System.out.println(response);
try {
List<JsonNode> devicesData = new ArrayList<>();
JsonNode jsonNode = ObjectMapperUtil.get().readTree(response);
if (jsonNode.has("error_code") && jsonNode.get("error_code").asInt() == 0) {
// System.out.println("list success");
JsonNode deviceListJson = jsonNode.get("hosts_info").get("host_info");
for (JsonNode node : deviceListJson) {
devicesData.add(node.get(node.fieldNames().next()));
}
}
System.out.println(devicesData);
return devicesData;
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
getDeviceList();
}
}
工具类OkHttpUtil
下面的代码中,只有postRaw
方法是对本文有用的,其他的你可以注释掉。这是我自己封装的OKhttp请求类。
package com.coderbbb.homeiot.utils;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class OkhttpUtil {
private static OkHttpClient client = null;
private static final Logger logger = LoggerFactory.getLogger(OkhttpUtil.class);
private synchronized static void createClient() {
if (client == null) {
OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
okHttpBuilder.protocols(Collections.singletonList(Protocol.HTTP_1_1));
okHttpBuilder.connectTimeout(60, TimeUnit.SECONDS);
okHttpBuilder.readTimeout(60, TimeUnit.SECONDS);
okHttpBuilder.writeTimeout(60, TimeUnit.SECONDS);
okHttpBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
//支持所有类型https请求
return true;
}
});
ConnectionPool pool = new ConnectionPool(200, 1, TimeUnit.SECONDS);
okHttpBuilder.connectionPool(pool);
client = okHttpBuilder.build();
client.dispatcher().setMaxRequests(2000);
client.dispatcher().setMaxRequestsPerHost(1000);
}
}
public static OkHttpClient getClient() {
if (client == null) {
createClient();
}
return client;
}
public static String get(String url, HashMap<String, String> headerMap) {
Request.Builder builder = new Request.Builder().url(url);
if (headerMap != null) {
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
builder.addHeader(entry.getKey(), entry.getValue());
}
}
Request request = builder.build();
String result = null;
try {
result = excute(request);
} catch (Exception e) {
logger.warn("http get fail:" + url + "###" + e.getMessage());
}
return result;
}
public static Response getAdvance(String url, HashMap<String, String> headerMap) throws Exception{
Request.Builder builder = new Request.Builder().url(url);
if (headerMap != null) {
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
builder.addHeader(entry.getKey(), entry.getValue());
}
}
Request request = builder.build();
return getClient().newCall(request).execute();
}
public static String postRaw(String url, String contentType, String json, HashMap<String, String> headerMap) {
RequestBody requestBody = RequestBody.create(json,MediaType.parse(contentType));
Request.Builder builder = new Request.Builder().url(url).post(requestBody);
if (headerMap != null) {
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
builder.addHeader(entry.getKey(), entry.getValue());
}
}
Request request = builder.build();
String result = null;
try {
result = excute(request);
} catch (Exception e) {
logger.error("http post raw fail:" + url + "###" + e.getMessage());
}
return result;
}
public static String post(String url, HashMap<String, String> data, HashMap<String, String> headerMap) {
FormBody.Builder formBodyBuilder = new FormBody.Builder();
for (Map.Entry<String, String> entry : data.entrySet()) {
formBodyBuilder.add(entry.getKey(), entry.getValue());
}
RequestBody requestBody = formBodyBuilder.build();
Request.Builder builder = new Request.Builder().url(url).post(requestBody);
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
builder.addHeader(entry.getKey(), entry.getValue());
}
Request request = builder.build();
String result = null;
try {
result = excute(request);
} catch (Exception e) {
logger.error("http post fail:" + url + "###" + e.getMessage());
}
return result;
}
private static String excute(Request request) throws Exception {
try (Response response = getClient().newCall(request).execute()) {
try (ResponseBody body = response.body()) {
if (body != null) {
String str = body.string();
// if(!response.headers("miot-content-encoding").isEmpty()){
// System.out.println("miot-content-encoding=" + response.headers("miot-content-encoding"));
// }
body.close();
response.close();
return str;
}
}catch (Exception e){
throw new Exception(e);
}
}catch (Exception e){
throw new Exception(e);
}
return null;
}
}