📜 ⬆️ ⬇️

Determining that you are at home using a WiFi router (to automate the smart home)

In a previous article, I described the climate control device on the ESP8266 . The question arises, and under what events should we carry out this management? The simplest is when a certain time comes.

The second thing that comes to mind is a presence in the house. If you are not at home, then there is no sense (or is there?) To air, heat and condition the room.

In this article we will consider the possibility of determining presence using a wifi router. No, we will not track people through the walls using the wifi signal , but use the status page in the wifi router's web interface, and by the presence of your smartphone in the list, we can understand whether you are at home or not.

Naturally, this method will not work if you, or someone from your family, does not use a smartphone, disables wifi, if you do not have wifi, when leaving, leave the device at home. The article also describes a “recipe” for a specific dlink router. If you have another model - it is likely that you will have to "modify the file."
')
A page displaying a list of wifi clients looks like this:



To access this page, we need to log in to the router. We study the source code of the password entry page and see:

1) in the password entry page the router sends salt and authid.
2) the router takes the first 16 digits from the password, combines them with salt, “finishes” the string with the chr (1) symbol up to 64 characters.
3) For the resulting 64x character string counts MD5.
4) combines salt + md5
5) forms a string like

http://192.168.0.1/post_login.xml?hash=a33403f9aded48e57FF9e09d37d9009026e1ce85&auth_code=&auth_id=09CFF 

where hash is a string obtained in p4., auth_id is a string obtained in n1.

6) if the authorization was successful, the router returns xml with the page address for the redirect.

Code about the following:

 var salt = 'a33403f9'; var password = document.forms.myform.old_password.value; password = password.substr(0,16); for (var i = password.length; i < 16; i++) { password += String.fromCharCode(1); } var input = salt + password; for (var i = input.length; i < 63; i++) { input += String.fromCharCode(1); } input += (document.forms.myform.old_username.value == 'user') ? 'U' : String.fromCharCode(1); var hash = hex_md5(input); var login_hash = salt.concat(hash); var auth_url = ''; auth_url = '&auth_code=' + document.forms.myform.auth_code.value + '&auth_id=09C05'; var xml_loader = new ajax_xmlhttp('/post_login.xml?hash=' + login_hash + auth_url, xml_ready, xml_timeout); 

After we logged in to the router’s page, it’s enough to request:

 http://192.168.0.1/wifi_assoc.xml 

and we get an XML of the form:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <wifi_assoc> <radio> <assoc> <mac>18FEFFFFCCFF</mac> <ssid>bingo</ssid> <channel>8</channel> <rate>65</rate> <quality>100</quality> <type>802.11n (2.4GHz)</type> <ip_address>192.168.0.3</ip_address> </assoc> <assoc> <mac>001DFEFF70FF</mac> <ssid>bingo</ssid> <channel>8</channel> <rate>65</rate> <quality>84</quality> <type>802.11n (2.4GHz)</type> <ip_address>192.168.0.4</ip_address> </assoc> <assoc> <mac>AC37FFFFDCFF</mac> <ssid>bingo</ssid> <channel>8</channel> <rate>104</rate> <quality>100</quality> <type>802.11n (2.4GHz)</type> <ip_address>192.168.0.5</ip_address> </assoc> <assoc> <mac>18FEFFFFFFDF</mac> <ssid>bingo</ssid> <channel>8</channel> <rate>58</rate> <quality>100</quality> <type>802.11n (2.4GHz)</type> <ip_address>192.168.0.6</ip_address> </assoc> </radio> </wifi_assoc> 

Having checked the availability of the MAC of your smartphone in this list, we can easily determine whether you * are at home or not.

The firmware for ESP8266 was such
* Due to restrictions on the amount of available memory, you cannot simply load pages into memory as a whole and perform search / parsing in memory. I had to use a primitive scanner to search for a pattern. I apologize in advance for the code from experts and thank you for the constructive suggestions for modification.
 #include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h> #include <MD5Builder.h> MD5Builder _md5; HTTPClient http; const char* ssid = "bingo"; const char* password = "qqq_zzz_xxx"; const char* ap_ssid = "esp"; const char* ap_password = "espqw3454#1"; // salt const char* patternSalt = "var salt = \""; const int patternsaltLen = strlen(patternSalt); int patternsaltPos = 0; char salt[20] = ""; int saltLen = sizeof(salt); int saltPos = 0; // auth const char* patternAuthID = "\"&auth_id="; const int patternAuthIDLen = strlen(patternAuthID); int patternAuthIDPos = 0; char authID[20] = ""; int authIDLen = sizeof(authID); int authIDPos = 0; // mac const char* patternMac = "<mac>"; const int patternMacLen = strlen(patternMac); int patternMacPos = 0; char mac[20] = ""; int macLen = sizeof(mac); int macPos = 0; typedef void (*patternSearchFinishedHandler)(); void patternSearchFinishedDummy() {} void patternSearchFinishedMac() { Serial.print("mac="); Serial.println(mac); mac[0] = (char) 0; macPos = 0; } void setup() { Serial.begin(115200); WiFi.softAP(ap_ssid, ap_password); WiFi.mode(WIFI_AP_STA); WiFi.begin(ssid, password); Serial.println("Setup Completed"); } void charBuf_to_u8buf(const char buf1[128], uint8_t buf2[128], int buffSize){ for (int i=0; i < buffSize; i++){ buf2[i] = (uint8_t)buf1[i]; } } void u8buf_to_charBuf(const uint8_t buf1[128], char buf2[128], int buffSize){ for (int i=0; i < buffSize; i++){ buf2[i] = (char)buf1[i]; } } void checkPattern(int* tpos, int tlen, char c, const char* templ, char* data, int* datapos, int datalen, char finishChar, patternSearchFinishedHandler handler){ if (*tpos == tlen) { if (finishChar == c){ if (patternSearchFinishedDummy != handler){ delay(10); handler(); } *tpos = 0; } else { if (*datapos < datalen-2){ data[*datapos] = c; data[*datapos + 1] = (char) 0; *datapos += 1; } } } else { if (templ[*tpos] == c){ *tpos += 1; }else{ *tpos = 0; } } } void processBuffer(uint8_t buff[128], int buffSize){ char cbuf[128] = {}; u8buf_to_charBuf(buff, cbuf, buffSize); for (int i=0; i < buffSize; i++){ checkPattern(&patternsaltPos, patternsaltLen, cbuf[i], patternSalt, salt, &saltPos, saltLen, '"', patternSearchFinishedDummy); checkPattern(&patternAuthIDPos, patternAuthIDLen, cbuf[i], patternAuthID, authID, &authIDPos, authIDLen, '"', patternSearchFinishedDummy); checkPattern(&patternMacPos, patternMacLen, cbuf[i], patternMac, mac, &macPos, macLen, '<', patternSearchFinishedMac); } } String md5(String str) { _md5.begin(); _md5.add(String(str)); _md5.calculate(); return _md5.toString(); } void intVars() { // init vars salt[0] = (char) 0; saltPos = 0; authID[0] = (char) 0; authIDPos = 0; mac[0] = (char) 0; macPos = 0; } void queryAddress(String address, bool dumpOutput, bool doProcessBuffer){ delay(10); // configure server and url http.begin(address); // Serial.print("[HTTP] GET...\n"); // start connection and send HTTP header int httpCode = http.GET(); if(httpCode > 0) { // HTTP header has been send and Server response header has been handled Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server if(httpCode == HTTP_CODE_OK) { // get lenght of document (is -1 when Server sends no Content-Length header) int len = http.getSize(); // create buffer for read uint8_t buff[128] = { 0 }; // get tcp stream WiFiClient * stream = http.getStreamPtr(); // read all data from server while(http.connected() && (len > 0 || len == -1)) { // get available data size size_t size = stream->available(); if(size) { // read up to 128 byte int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); if (doProcessBuffer){ processBuffer(buff,c); } // write it to Serial if (dumpOutput){ Serial.write(buff, c); } if(len > 0) { len -= c; } } delay(10); } Serial.println(); Serial.print("[HTTP] connection closed or file end.\n"); } } else { Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } http.end(); } void queryData(){ intVars(); queryAddress("http://192.168.0.1/", false, true); Serial.print("salt="); Serial.println(salt); Serial.print("authID="); Serial.println(authID); String data = ""; data.concat(salt); // password data.concat("bingo_fff#xxx "); data = md5(data); String addr = "http://192.168.0.1/post_login.xml?hash="; addr.concat(salt); addr.concat(data); addr.concat("&auth_code=&auth_id="); addr.concat(authID); queryAddress(addr, false, false); queryAddress("http://192.168.0.1/wifi_assoc.xml", false, true); delay(10000); } void loop() { // Wait for connection if (WiFi.status() == WL_CONNECTED) { queryData(); } else { delay(500); Serial.print("."); } } 

The firmware periodically displays a list of connected devices in the console.

Source: https://habr.com/ru/post/357916/


All Articles