WiFiルーターを使用して自宅にいると判断する(スマートホームを自動化するため)

前の記事で、ESP8266の温度制御デバイスについて説明しました 。 この管理をどのようなイベントで実行すべきかという疑問が生じます。 最も簡単なことは、特定の時間が来るときです。



頭に浮かぶ2番目のことは、家の中の存在です。 自宅にいない場合は、部屋を換気、暖房、空調する必要はありません(またはありますか?)。



この記事では、wifiルーターを使用して存在を判断する可能性を検討します。 いいえ、 wifi信号を使用して壁を通り抜ける人を追跡しませんが、wifiルーターのウェブインターフェースのステータスページを使用します。スマートフォンのリストに表示されることで、自宅にいるかどうかを確認できます。



当然、この方法は、あなたまたはあなたの家族の誰かがスマートフォンを使用せず、wifiを切断した場合、wifiを持っていない場合、家を離れる場合は機能しません。 また、この記事では、特定のdlinkルーターの「レシピ」について説明しています。 別のモデルを使用している場合は、「ファイルを変更する必要がある可能性があります。



wifiクライアントのリストを表示するページは次のようになります。







このページにアクセスするには、ルーターにログインする必要があります。 パスワード入力ページのソースコードを調べて、以下を確認します。



1)パスワード入力ページで、ルーターはsaltとauthidを送信します。

2)ルータはパスワードから最初の16桁を取得し、それらをソルトと結合し、最大64文字のchr(1)記号で文字列を「仕上げ」ます。

3)受信した64x文字列の場合、MD5がカウントします。

4)salt + md5を組み合わせる

5)フォームの文字列を形成します



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





hashはp4で受信した文字列、auth_idはp1で受信した文字列です。



6)認証が成功した場合、ルーターはリダイレクトのページアドレスを含むxmlを返します。



コードは次のようなものです。



 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);
      
      





ルーターのページで承認されたら、次をリクエストするだけで十分です。



 http://192.168.0.1/wifi_assoc.xml
      
      





そして、次の形式のXMLを取得します。



 <?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>
      
      





このリストでスマートフォンのMACの可用性を確認することにより、自宅にいるかどうかを簡単に判断できます。



ESP8266のファームウェアは次のようになりました
*使用可能なメモリの量には制限があるため、ページ全体をメモリにロードして、メモリ内で検索/解析することはできません。 テンプレートで検索するには、プリミティブスキャナーを使用する必要がありました。 コードの専門家に事前に謝罪し、修正のための建設的な提案に感謝します。

 #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("."); } }
      
      





ファームウェアは、接続されたデバイスのリストをコンソールに定期的に表示します。




All Articles