/*
 Version 0.1 - 15/08/2019   
 SONOFF basic r2, 
 SINRIC,
 ALEXA
 bouton + relai + led
*/ 

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>   //  https://github.com/kakopappa/sinric/wiki/How-to-add-dependency-libraries
#include <ArduinoJson.h>        // https://github.com/kakopappa/sinric/wiki/How-to-add-dependency-libraries
#include <StreamString.h>

#include <AceButton.h>          // https://github.com/bxparks/AceButton
using namespace ace_button;

ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
WiFiClient client;

#define HEARTBEAT_INTERVAL 150000
          
#define MyApiKey "xxxxxxxxxxxxxxxxxxxx"   // Changez votre cle API. votre cle API est affiché dans votre compte sur sinric.com
#define MySSID "xxxxxxxx"                 // Changez votre reseau Wifi
#define MyWifiPassword "xxxxxxxx"         // Changez votre mot de passe reseau Wifi 
#define Switch1 "xxxxxxxxx"               // Changez l'ID par celuis de votre switch crée sur sinric.com

const int LED_PIN = 13;     
const int BUTTON_PIN = 0;
const int RELAY_PIN = 12;

uint64_t heartbeatTimestamp = 0;
bool isConnected = false;
bool flag;

AceButton button(BUTTON_PIN);
 
void handleEvent(AceButton*, uint8_t, uint8_t);
void setPowerStateOnServer(String deviceId, String value);

void turnOn(String deviceId) {
  if (deviceId == Switch1) 
  {  
    Serial.println("ON par sinric ou alexa");
    
    digitalWrite(LED_PIN, LOW);
    digitalWrite(RELAY_PIN, HIGH);
    flag = false;
  }    
}

void turnOff(String deviceId) {
   if (deviceId == Switch1) 
   {  
     Serial.println("OFF par sinric ou alexa");     
     digitalWrite(LED_PIN, HIGH); 
     digitalWrite(RELAY_PIN, LOW); 
     flag = true;
   }  
}

void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
  switch(type) {
    case WStype_DISCONNECTED:
      isConnected = false;    
      Serial.printf("[WSc] Webservice disconnected from sinric.com!\n");
      break;
    case WStype_CONNECTED: {
      isConnected = true;
      Serial.printf("[WSc] Service connected to sinric.com at url: %s\n", payload);
      Serial.printf("Waiting for commands from sinric.com ...\n");        
      }
      break;
    case WStype_TEXT: {
        Serial.printf("[WSc] get text: %s\n", payload);          
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject((char*)payload); 
        String deviceId = json ["deviceId"];     
        String action = json ["action"];        
        if(action == "setPowerState") { // Switch or Light
            String value = json ["value"];
            if(value == "ON") {
                turnOn(deviceId);
            } else {
                turnOff(deviceId);
            }
        }
        else if (action == "test") {
            Serial.println("[WSc] received test command from sinric.com");
        }
      }
      break;
    case WStype_BIN:
      Serial.printf("[WSc] get binary length: %u\n", length);
      break;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, HIGH);
  delay(5000);
  WiFiMulti.addAP(MySSID, MyWifiPassword);
  Serial.println();
  Serial.print("Connecting to Wifi: ");
  Serial.println(MySSID);    
  // Waiting for Wifi connect
  while(WiFiMulti.run() != WL_CONNECTED) {
    digitalWrite(LED_PIN, HIGH);
    delay(100);
    Serial.print(".");
    digitalWrite(LED_PIN, LOW);
    delay(100);
  }
  if(WiFiMulti.run() == WL_CONNECTED) {
    Serial.println("");
    Serial.print("WiFi connected. ");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());    
  }
  digitalWrite(LED_PIN, LOW);  
  pinMode(BUTTON_PIN, INPUT); 
  button.init(BUTTON_PIN);
  button.setEventHandler(handleEvent);
 
  webSocket.begin("iot.sinric.com", 80, "/");  
  webSocket.onEvent(webSocketEvent);
  webSocket.setAuthorization("apikey", MyApiKey);  
  webSocket.setReconnectInterval(5000);  
}

void loop() {
  webSocket.loop();
  button.check();
  if(isConnected) {
      uint64_t now = millis();       
      if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) {
          heartbeatTimestamp = now;
          webSocket.sendTXT("H");          
      }
  }   
}
 
void setPowerStateOnServer(String deviceId, String value) {
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["deviceId"] = deviceId;
  root["action"] = "setPowerState";
  root["value"] = value;
  StreamString databuf;
  root.printTo(databuf);  
  webSocket.sendTXT(databuf);
}

void handleEvent(AceButton*, uint8_t eventType, uint8_t) {
  switch (eventType) {
    case AceButton::kEventPressed:
      Serial.println("kEventPressed");
      if (flag == true) {
        setPowerStateOnServer(Switch1, "ON");
        Serial.println("ON par le bouton");
        digitalWrite(LED_PIN, LOW);
        digitalWrite(RELAY_PIN, HIGH);
        flag = false;              
        break; 
      } else {
        setPowerStateOnServer(Switch1, "OFF");
        Serial.println("OFF par le bouton");
        digitalWrite(LED_PIN, HIGH); 
        digitalWrite(RELAY_PIN, LOW);
        flag = true;                
        break;   
      }
    case AceButton::kEventReleased:
          Serial.println("kEventReleased");
          break;
  }
}