Второе что приходит в голову — присутствие в доме. Если вас нет дома, то нет смысла (или есть?) проветривать, отапливать и кондиционировать помещение.
В этой статье рассмотрим возможность определения присутствия используя wifi роутер. Нет, мы не будем следить за людьми сквозь стены используя wifi сигнал, а воспользуемся страничкой состояния в веб интерфейсе wifi роутера, и по наличию в списке вашего смартфона сможем понять дома вы или нет.
Естественно этот способ не сработает, если вы, или кто-то из вашей семьи, не использует смартфон, отключает wifi, если у вас нет wifi, уходя, оставляет устройство дома. Также в статье описан «рецепт» для конкретного dlink роутера. Если у вас другая модель — вероятно, что вам придется «доработать напильником».
Страница, отображающая список wifi клиентов выглядит примерно так:

Для доступа к этой странице нам необходимо авторизоваться на роутере. Изучаем исходный код страницы ввода пароля и видим:
1) в странице ввода пароля роутер отправляет salt и authid.
2) роутер берет из пароля первые 16 цифр, объединяет их с salt, «добивает» строку символом chr(1) до 64х символов.
3) Для полученной 64x символьной строки считает MD5.
4) объединяет salt + md5
5) формирует строку вида
http://192.168.0.1/post_login.xml?hash=a33403f9aded48e57FF9e09d37d9009026e1ce85&auth_code=&auth_id=09CFF
где hash это строка полученная в п4., auth_id — строка полученная в п1.
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 своего смартфона в этом списке, мы легко определим дома вы* или нет.
#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(".");
  }
}
Прошивка периодически выводит в консоль список подключенных устройств.
Комментарии (23)
 - proton1706.03.2017 12:25- Очень много "если" в таком способе. Если говорить про "Умный дом", то достаточно 3-4 датчиков присутствия и не важно есть ли у кого-то смартфон и включен ли там Wi-Fi.  - LexB06.03.2017 12:36- Согласен — датчики надежнее, фундаментальнее. Это умный дом без кавычек. 
 С другой стороны датчики надо приобрести, разместить, настроить, запитать, обслуживать. А тут вообще без вложений можно покрыть текущие нужды, из разряда «дешево и сердито», «умный дом» в кавычках.- DnD_designer06.03.2017 14:46- Датчики движения могут выполнять еще одну полезную функцию — охранной сигнализации в ваше отсутсвие (особенно, на момент отпуска или командировки). 
 
 
 - instalator06.03.2017 12:41+1- У себя я просто использую пинг телефона. Можно использовать приложение для андроид телефона — таскер и при подключении к домашней сети выдавать команду серверу УД. 
 - memtew06.03.2017 12:46+1- Опция dhcp-scipt у Dnsmasq позволяет при выдаче каждого IP выполнять произвольный скрипт, чем я и пользуюсь. 
 - telobezumnoe06.03.2017 13:48+1- на есп8266 в скетче использую библиотеку ping раз в десять секунд проверяю нужный мне айпишник, который вручную забит на телефоне и находится вне диапазона выдаваемого dhcp. при отсутствии меня дома или жены, публикуется статус в mqtt и включается ip cam и уже по движению ведет запись. всего пару строк кода, и схожий функционал. 
- AllexIn06.03.2017 13:58+3- Все таки ИМХО пожарную-охранную сигнализацию надо ставить первым делом. За долго до контроллеров лампочек и аудиосистем. А дальше — уже просто. Дом на охране — значит вас в нем нет. 
- geisha06.03.2017 14:00- Вообще-то ESPшка может хватать пакеты из эфира на штатной прошивке. Там MAC plain-textом записан. Минусы: не будет работать на 5Ghz и если телефон отключает WiFi при выключенном экране. - safari201206.03.2017 15:12+1- Я бы вообще не стал вешать ESP-шку на общий SSID с другими устройствами. Лучше её выделить в отдельный SSID, благо большинство современных роутеров поддерживают виртуальные wi-fi сети. 
 Не знаю, как от одной, но от нескольких ESP-шек скорость wi-fi сильно проседает. Это известная проблема реализации стека на данном MCU. - vvzvlad06.03.2017 17:04- А что, проседание скорости на виртуальной сети, которая висит на том же физическом радио, никак не затронет основную сеть? 
 - geisha06.03.2017 18:12- Я про promiscuous mode. Ваш комментарий я не совсем понял, ибо, в моём представлении, ESPшка молчит и ничего сама в эфир не шлёт. 
 
 
 - Artemiy11706.03.2017 16:40-1- А как быть если телефон сел или потерял сеть? 
 Ночью, например телефон сел, и еспишка включила охрану. Ночью пошел в туалет, а тут тебе сирена врубилась. :)
 - danyaShep06.03.2017 21:54-1- И почему все первым делом изобретают велосипед? 
 https://home-assistant.io/components/#presence-detection
 - IRT07.03.2017 07:43- Надежнее датчик движения в прихожей + геркон на входной двери. Закрылась дверь и нет движения в прихожей = дома никого нет.  - LexB07.03.2017 10:59- Тоже об этом думал. Сработает если вы холостяк, иначе датчики надо ставить во всех комнатах, и то, если супруга не шевелится (спит) алгоритм не сработает. 
 
 
           
 
DaemonGloom
А в ESP8266 не работают стандартные методы определения IP устройств по mac адресу?
Обычно просто отправляют ICMP ECHO по адресу 255.255.255.255 и слушают ответы.
http://electronics.stackexchange.com/questions/170248/how-to-get-ip-address-from-mac-address-using-esp8266-wifi-module
При этом уберёте кучу зависимостей от конкретного роутера и прошивки.
vvzvlad
Ха, и напоретесь на интересное поведение девайсов от Apple(может и от других тоже) — они с включённым wifi перестают отвечать на пинги через минут десять после засыпания/выключения. Более того, они вроде и аренду не продлевают. Единственное место, откуда их можно вытащить — список подключённых клиентов(wifi associated list, емнип, в микротиках называется). Все остальное работает ненадежно.