Passons aux choses sérieuses ...

Schéma du module et de son "Pyroelectric (ou Passive) InfraRed sensor" ou capteur PIR , capteur détecteur de mouvement:



Le mini detecteur infra rouge : (HC-SR505 Mini Infrared PIR Motion Sensor) présente l'avantage d'avoir sa sortie en 3v avec une temporisation d'environ 8 secondes non règlables.
Si vous utilisez un autre PIR avec une sortie 5v ou sans temporisation, attention, il faudra faire un pont diviseur avec 2 résistances (2k et 1k) ou gérer éventuellement par programme la temporisation.
L'alimentation est a prévoir en phase finale, en cours de développement le module est alimenté par l'USB de l'ordi.
Pour l'alim. j'utilise un ancien chargeur téléphone 220v/5v.


witty_temp.zip

Quelques explications sur le programme:

D'abord les librairies,
#include <ESP8266WiFi.h>            // pour la connexion WiFi
#include <ESP8266WebServer.h>       // Pour l'ecoute et la communication sur le port (localPort)
#include <WiFiClientSecure.h>       // pour l'envoi de https 
#include <WiFiUdp.h>                // pour récuperer la date et l'heure sur un time serveur 
#include <TimeLib.h>                // pour  synchroniser  l'horloge (millis) de l'esp8266 sur le temps universel(UTC)
#include "ThingSpeak.h"             // Pour archiver les stats sur cloud (gratuit un an renouvelable)
#include "Gsender.h"                // pour l'envoi de mails, attention bien mettre entre guillemets, modifier Gsender.h
                                    // (identifiant et mot de passe) et mettre Gsender.cpp et Gsender.h  dans le dossier de ce sketch.
Puis les pages stockées en flash
#include "chart.h"                  // Page Web pour client, affichage des stats sur 24 heures
#include "err404.h"                 // Page Web erreur 404

Avant le téléversement ou la compilation vérifier que tous les onglets sont présents


Les déclarations, " les parametres perso "
const char* vers = " Version: 201707302249";
int LDR = A0;                       // mesure de la lumiere ambiante en %
int luminositeTemp;                 // valeur max de luminosité dans le créneau de 2mn
const int rouge = 15;               // pour tests de fonctionnement
const int bleue = 12;               // les gpio bleue/verte varient en fonction des modeles
const int verte = 13;               // bleue/verte pour tests de fonctionnement
bool visible = true;                // sur les modules witty-gizwits active ou pas les leds pour Debug
unsigned int i = 0;
unsigned int count = 0;             // nombre de détections depuis la reinitialisation
//************************ WiFi  *************************
const char* ssid = "XXXXXXXX";
const char* password = "XXXXXXXX";
unsigned int localPort = 49999;     // pour  acces par internet ( parametrer routeur ou box , baux statiques IP , et redirection des ports)
// attention aux ports serveurs (8080,8081, etc....) tres interrogés par les robots! (koréens, Russes, USA etc....)
// définir dans la plage des ports privés ( comprise entre 49152 et 65535 )
const char* zone = "Entrée";        // zone ou est placé le detecteur
ESP8266WebServer server(localPort); // ecoute le port localPort
//**********************   email free SMTP  **********************
bool flag_email = false;
// Activation du SMTP authentifié,  a activer sur https://subscribe.free.fr/login/ ,gérer mes emails,
// puis  renseigner ( comme noté à  la suite) votre compte smtp dans Gsender.h dans le repertoire (dossier) de ce sketch
//const int SMTP_PORT = 465; // securisé
//const char* SMTP_SERVER = "smtp.free.fr";
//const char* EMAILBASE64_LOGIN = "xxxxxxxxxxxxxxxxxx";// en base64
//const char* EMAILBASE64_PASSWORD = "xxxxxxxxxxxxxxxx";// en base64
//const char* FROM = "xxxxxxxxxxx@free.fr";// en clair l'adresse a laquelle vous envoyez l'Email

// ATTENTION !... pour encoder en base64, un soft a télécharcher, https://sourceforge.net/projects/gbm64/ , gratuit
// pour votre sécurité évitez de convertir en ligne en base64 vos identifiants et mots de passe
// Il existe aussi une librairie  sur github

//****************** paramètres du serveur sms free-mobile ************************
bool flag_sms = false;
const char* host = "smsapi.free-mobile.fr";
const int httpsPort = 443;
const char* fingerprint = "18 EB 36 0D AD 38 0D 0E D1 87 8B 09 4D 44 CF 45 38 90 F7 DC";
// ----------------------- paramètres abonné -------------------------------
const String user = "XXXXXXXX";       // identifiant abonné
const String pass = "XXXXXXXX"; // pour obtenir un nouveau password (desactiver/activer le service chez FREE)
//****************************  Commandes  ************************
// Paramètres et commandes acceptés a modifier selon votre projet!... exemple avec PIN: "/1234sms_on"....etc...
// les 2 premiers ne doivent pas être modifiés,( "/" et "/favicon.ico" )
const char* uriArray[15] = {"/", "/favicon.ico", "/sms_on", "/sms_off", "/email_on", "/email_off", "/bleue_on", "/bleue_off", "/alarme_on", "/alarme_off", "/visible_on", "/visible_off", "/check", "/reset", "/check24"};
String message = "";                  // message en retour
String reponse = "";                  // message  encodé (web,sms ou mail)
//************************* UDP NTP TIME  **************************
// ntp-p1.obspm.fr (IP : 145.238.203.14) : serveur primaire (de strate 1) installé au LNE-SYRTE (campus parisien de l'Observatoire).
IPAddress timeServer(145, 238, 203, 14);
//IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
int timeZone = 0;                     // UTC=0, Heure Europe centrale +1(heure d'hiver) +2 pour l'été en france
const int NTP_PACKET_SIZE = 48;       // L'horodatage NTP est dans les 48 premiers octets du message
byte packetBuffer[ NTP_PACKET_SIZE];  //Buffer pour contenir les paquets entrants et sortants
WiFiUDP Udp;
//--------------------------------  Time  ----------------------------------
// Mettre en français les mois et les jours dans la librairie Time, DateStrings.h
String strDateInit = "";              // Date au redémarrage
String strDatePir = "";               //derniere detection pir
int anneeEnCours;
unsigned long aujourdHui;
unsigned long heureEte;
unsigned long heureHiver;
unsigned long heureCount;

//************************* detection PIR *************************
const int pir = 14;                   // raccordé a la sortie du PIR ( attention 3.3v max) si PIR 5v branchement: gnd_____2k_____gpio14______1k_____PIRdata
bool flag_pir = false;                // déclenché par une présence
bool flag_alarme = false;             // active l'alarme
//************************* Thingspeak *************************
int delayThingspeak = 120;            //120 secondes
const char* serverThingspeak = "api.thingspeak.com";
unsigned long channel = XXXXXXXX;
const char * apiKey = "XXXXXXXX";
unsigned long timeThingspeak ; 
//*************************  fin des déclarations  ***********************
L'initialisation du module
void setup()
{
  pinMode(rouge, OUTPUT);
  pinMode(bleue, OUTPUT);
  pinMode(verte, OUTPUT);
  pinMode(pir, INPUT);
  digitalWrite(rouge, !visible);
  digitalWrite(bleue, !visible);
  digitalWrite(verte, !visible);
  visible = false;
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  //Serial.println("");// Debug

  while (WiFi.status() != WL_CONNECTED) {
    //Serial.print(".");// Debug
    digitalWrite(rouge, 1); delay(250); digitalWrite(rouge, 0); delay(250);
  }
  Serial.println("");// Debug
  Serial.print("Connecte a ");// Debug
  Serial.println(ssid);// Debug
  Serial.print("IP adresse: ");// Debug
  Serial.println(WiFi.localIP());// Debug

  Serial.println("Start UDP");
  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("Synchronisation en cours");
  setSyncProvider(getNtpTime);//utc timeZone =0
  count = readThingspeak(3); // récupere derniere date et heure de comptage en cas de reinit dans la journée
  Serial.print("compteur de detections: ");
  Serial.println(count) ;
  calculTimeZone();  // calcul heure d'été ou d'hiver
  setSyncInterval ( 300 ); // Synchronise toutes les 300 secondes
  strDateInit = actualStringDate(); // date et heure de l'initialisation
  timeThingspeak = now(); // Initialise l'horloge de l'archivage, prochain dans 120s  
  checkParam(); // affiche les parametres actuels  server.begin(); // démarre l'écoute de local port  //-----------------------------------------------------------------------------------------------  server.on(uriArray[0], []() {    message = " Bonjour ! L'accès à  cette page nécessite des autorisations .....";    envoi_reponse(200);  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[1], []() {    envoi_reponse(404);  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[2], []() {    flag_sms = true;    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[3], []() {    flag_sms = false;    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[4], []() {    flag_email = true;    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[5], []() {    flag_email = false;    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[6], []() {    digitalWrite(bleue, 1);    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[7], []() {    digitalWrite(bleue, 0);    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[8], []() {    flag_alarme = true;    flag_email = true;    flag_sms = true;    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[9], []() {    flag_alarme = false;    //flag_email =  false;    //flag_sms = false;    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[10], []() {    visible = true;    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[11], []() {    visible = false;    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[12], []() {    checkParam();  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[13], []() {    message = " Bonjour ! Le sytème va être réinitialisé dans 2 secondes et un SMS sera envoyé";    envoi_reponse(200);    delay(2000);    ESP.restart();  // RESET  });  //-----------------------------------------------------------------------------------------------  server.on(uriArray[14], []() {    checkParam24();  });  //-----------------------------------------------------------------------------------------------  server.onNotFound( []() { // requête inconnue ?...... Analyse de de la requête et envoi de sms si activé (sms_on)    server.send(404, "text/html", err404);  });  //--------------  fin du setup et envoi du message de réinitialisation  (email et sms)-----------------  flag_sms = true;  flag_email = true;  message = "REINITIALISATION : " + String(zone) + " , mode visible désactivé, attention! l'ALERTE PIR par mail et sms va être désactivée après ce message ";  envoi_reponse(200);  flag_sms = false;  flag_email = false;  delay(10000);// attend la fin detection pir (environ 8s) }
La boucle
void loop() {
  if (hour() == 23 && minute() == 59 && (second() >= 50))count = 0; // met le compteur de détections a 0, pendant 10s avant minuit
  if  (WiFi.status() == WL_CONNECTED) { //  si coupure temporaire de WIFI, inutile de faire les lignes suivantes)
    server.handleClient();
    if ( (millis() % 3000 == 0) && (luminositeTemp <= luminosite()))  luminositeTemp = luminosite() ; // toutes les 3 secondes m.a.j luminosité max
    digitalWrite(verte, ( flag_pir & visible));  // éclaire led verte, détection pir en mode visible
    if ( now() >= (timeThingspeak + delayThingspeak))sendThingspeak();// envoi des datas stats vers thingspeak.com toutes les 2mn
    if (digitalRead(pir)) pir_alarme();  // déclenche l'alarme si activée
    if ((now() > heureEte && now() < heureHiver && timeZone == 1) || (now() > heureHiver && timeZone == 2) || anneeEnCours != year() || timeZone > 2 || timeZone < 1 ) calculTimeZone(); // Recalcule timezone si fausse (dépassement millis()) ou  au changement d'année
  }
}

Les fonctions
Lorsque vous envoyez une commande, exemple:   http://121.247.246.120:8081/check   ou en local    http://192.168.0.25:8081/check
le serveur a l'écoute du port 8081 lit l'uri "/check",
uriArray[12] contient la string "/check"
cette partie du programme  est concernée, checkParam() est lancé :
//-----------------------------------------------------------------------------------------------
server.on(uriArray[12], []() {
    checkParam();
  });
//-----------------------------------------------------------------------------------------------					
checkParam() s'exécute et le module vous répond:
//********************* Renvoie les paramètres en cours **************************
void checkParam() {
  param();
  //Serial.println(message);// Debug
  envoi_reponse(200);
}
//********************************************************************************************************
String param() {
  message = vers ; //  version du sketch
  message +=  "  " + actualStringDate() + "\n";
  message += "Paramètres actuels du détecteur de la maison: " + String(zone);
  message += "\n";
  message += "Reinitialisé le: " + strDateInit;
  message += "\n";
  message += "Nombre de détections depuis minuit: " +  String(count);
  message += "\n";
  message += "LUMINOSITE:" + String(luminositeTemp) + "%";
  message += "\n";
  message += (flag_sms) ? "SMS ACTIVES" : "SMS désactivés" ;
  message += "\n";
  message += (flag_email) ? "EMAILS ACTIVES" : "EMAILS désactivés" ;
  message += "\n";
  message += (digitalRead(bleue)) ? "LED BLEUE ALLUMEE" : "LED BLEUE éteinte" ;
  message += "\n";
  message += (visible) ?  "Mode visible ACTIVE (les témoins lumineux s'allument lors de détections)" : "Mode visible désactivé (aucun témoin lumineux allumé automatiquement)";
  message += "\n";
  message += (flag_alarme) ? "ALARME ACTIVE " : "ALARME désactivée " ;
  message += "\n";
  message += "Dernière détection après réinitialisation le: " + strDatePir;
  message += "\n";
  return  message;

Réponse:
Version: 201707151312 Lundi 07/08/2017 à 08:36:17
Paramètres actuels du détecteur de la maison: Entrée
Reinitialisé le: Samedi 15/07/2017 à 18:57:39
Nombre de détections depuis minuit: 12
LUMINOSITE:7%
SMS désactivés
EMAILS désactivés
LED BLEUE éteinte
Mode visible ACTIVE (les témoins lumineux s'allument lors de détections)
ALARME désactivée 
Dernière détection après réinitialisation le: Lundi 07/08/2017 à 08:23:21

Lecture et écriture sur le compte thingspeak.com, utilisé comme base de données, les réponses sont au format json:
//**************************************** Envoi des datas vers le compte thingspeak.com ********************************************************
void sendThingspeak()
{
  timeThingspeak = now();
  count  += flag_pir; //  incrémente le nbre de détections si une détection a eu lieu dans les 2mn
  writeThingspeak(flag_pir, luminositeTemp, count);
  flag_pir = false;
  luminositeTemp = 0;
}
//**************************************** écrit marqueur reinit + lit la date du dernier enregistrement  ********************************************************
int readThingspeak(int field) // retourne le nombre  de detections si c'est le meme jour,  sinon 0
{ WiFiClient client;
  ThingSpeak.begin(client);
  ThingSpeak.setField(4, 1);
  ThingSpeak.writeFields(channel, apiKey);// envoie 1 pour marqeur de reinitialisation
  int fieldInf = ThingSpeak.readIntField(channel, field, apiKey);
  if ( ThingSpeak.readCreatedAt(channel, apiKey).substring(0, 10) != String(year()) + "-" + toDigits(month()) + "-" + toDigits(day())) // compare date UTC du jour et date dernier enregistrement
  {
    fieldInf = 0;
  }
  return fieldInf;
}
//**************************************** ecrit des datas sur le compte thingspeak.com ********************************************************
void writeThingspeak(int field1, int field2, int field3)
{ WiFiClient client;
  ThingSpeak.begin(client);
  ThingSpeak.setField(1, field1);
  ThingSpeak.setField(2, field2);
  ThingSpeak.setField(3, field3);
  ThingSpeak.writeFields(channel, apiKey);
}
//**************************************** Fin compte thingspeak.com *******************************************************

Formate la date en français exemple: Lundi 07/08/2017 à 08:36:17 au lieu de Lundi 7/8/2017 à 8:36:17
//*******************************  formate date ***************************************
String actualStringDate() { // formate l'affichage de la date et de l'heure en Français
  String strDate = String(dayStr(weekday())) + " "; // a mettre en français dans la librairie Time, DateStrings.h
  strDate += toDigits(day()) +  "/" + toDigits(month()) +  "/" + String(year()) + " à ";
  strDate += toDigits(hour()) + ":" + toDigits(minute()) + ":" + toDigits(second());
  return strDate;
}
//************************ affiche les nombres 0->9 , 00->09 a deux "digits"********************
String toDigits(int digits) {
  String s = "";
  s = (digits < 10) ? "0" + String(digits) : String(digits);
  return s;
}


Détection de mouvement et envoi un message d'alarme SMS et MAIL si cette commande a été envoyée précédemment, exemple:   http://121.247.246.120:8081/alarme_on   ou en local    http://192.168.0.25:8081/alarme_on
//********************************   Détection de mouvement PIR   ************************************************
void pir_alarme() {  //  déclenche l'alarme si activée
  if (!flag_pir ) {
    flag_pir = true;
    strDatePir = actualStringDate();

    Serial.print(" Detection PIR ! ");// Debug
    Serial.println(count);// Debug
    digitalWrite(verte, 0); digitalWrite(rouge, visible); delay(500); digitalWrite(rouge, 0); // Debug

    if (flag_alarme) { // si uri "/alarme_on"
      flag_sms = true; flag_email = true;
      message = "Alarme détection de présence!...port: " + String(localPort) + "   " + String(zone);
      envoi_reponse(200);
      checkParam();
      flag_sms = false; flag_email = false;
    }
  }
}

Exemple de réalisation pratique

L'intégration dans le boitier d'un CPL Hors Sevice, permet facilement la réalisation du projet:



Vue de l'intérieur, les éléments sont collés (mastic silicone pour l'alim et le pir)



Quelques gouttes de colle cyanoacrylate (super glue) suffiront a fixer Witty face a l'ouverture de l'ancienne prise ethernet. l'interface série (interface board) a été gardé pour la mise a jour du logiciel.
Les pattes du capteur PIR ont été redressées pour une sortie a 90°
L'alimentation 220v/5v est de la récup d'un ancien chargeur de téléphone, ce module peut etre remplacé par n'importe quel autre 5v délivrant au moins 500mA.   exemple ici.
Pour ceux qui opteraient pour plus de sécurité, utilisez un petit boitier standard alimenté par un chargeur de téléphone 5V microUSB.
2 petits cylindres de matiere plastique transparents servent de guide de lumière vers la façade pour la LED 3 couleur et la LDR (photorésistance).
Et il reste encore beaucoup de place dans le boitier!
Au démarrage la led rouge clignotte tant que la connection WIFI n'est pas effectuée.