#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#include <FS.h>
#include <WebSocketsClient.h>
#include <ArduinoJson.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Ticker.h>

#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>

WebSocketsClient webSocket;
Ticker myTicker;

// 温度输入脚接开发板数字引脚13
#define d7 13
OneWire oneWire(d7);
DallasTemperature sensors(&oneWire);
//普通开关
int d1 = 5;

//加执
int d2 = 4;
//散温
int d5 = 14;

//加湿开关
int d6 = 12;
//开关状态
String state;

#define DHTPIN 3     // 数字引脚连接DHT传感器
#define DHTTYPE    DHT11     // DHT 11
DHT_Unified dht(DHTPIN, DHTTYPE);
//最近操作物联用户相关信息
String UserExec = "";
//几个全局变量
String str;
DynamicJsonDocument  jsonBuffer(200);
JsonObject root;


//被读取的文件位置和名称
String flash_wifi = "/wifi.txt";
ESP8266WebServer ESP8266webServer(80);  // 创建网络服务器对象，HTTP服务的标准端口为：80
String ssid = "";
String passwd = "";

String html = String("") + "<!DOCTYPE html>" +
              "<html>" +
              "<head>" +
              "<meta charset='utf-8'>" +
              "<meta name='viewport' content='width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no' />" +
              "<title>设置WiFi</title>" +
              "<link rel='stylesheet' href=''>" +
              "</head>" +
              "<script>" +
              "onload  = function() {" +
              "document.getElementById('up').onclick = function(){" +
              "var ssid=document.getElementsByTagName('input')[0].value;" +
              "var passwd=document.getElementsByTagName('input')[1].value;" +
              "xhr = new XMLHttpRequest();" +
              "xhr.open('POST','./set_wifi',true);" +
              "var posts = ('ssid='+ssid+'&passwd='+ passwd);" +
              "xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');" +
              "xhr.send(posts);" +
              "xhr.onreadystatechange = function(){" +
              " if(this.readyState == 4 && this.status == 200){" +
              "alert(this.responseText);" +
              " }" +
              " }" +
              "}" +
              "}" +
              "</script>" +
              "<body>" +
              "<p>WiFi帐号<input type='text' name='ssid'></p>" +
              "<p>WiFi密码<input type='text' name='passwd'></p>" +
              "<input type='submit' id='up' value='设置'>" +
              "<div>连接成功AP温点退出转WIFI_STA</div>" +
              "</body>" +
              "</html>";

float DHTt;
float DHTh;
int DHTtMax; //环境温度最大值
int DHTtMin; //环境温度最小值
int DHThMax; //环境湿度最大值
int DHThMin; //环境湿度最小值
float DS18B20;//DS18B20感温
int temperature = 9;
int i = 0;
bool show = false;
void setup(void) {
  pinMode(d1, OUTPUT);
  pinMode(d2, OUTPUT);
  pinMode(d5, OUTPUT);
  pinMode(d6, OUTPUT);

  digitalWrite(d1, LOW);
  digitalWrite(d2, LOW);
  digitalWrite(d5, LOW);
  digitalWrite(d6, LOW);

  dht.begin();
  sensor_t sensor;
  delay(1000);

  bool wifibool = wifi_bool();
  if (wifibool == false) {
    ap_start();
    delay(1000);
    Serial.println("bei wang");
    webserver_start();
    while (1) {
      ESP8266webServer.handleClient();   // 检查网络服务请求并处理
    }
  }

  //灵活设置温度触发开关值,启动时先获取大棚散温触发温度值
  String temp = http_get("http://www.360xyz.cn/automation/DapengDHT");
  deserializeJson(jsonBuffer, temp);
  root = jsonBuffer.as<JsonObject>();

  String tmax = root["DHTtMax"];
  String tmin = root["DHTtMin"];
  DHTtMax = tmax.toInt();
  DHTtMin = tmin.toInt();

  String hmax = root["DHThMax"];
  String hmin = root["DHThMin"];
  DHThMax = hmax.toInt();
  DHThMin = hmin.toInt();
  String B20 = root["DS18B20"];
  DS18B20 = B20.toInt();
  // server address, port and URL
  webSocket.beginSSL("www.360xyz.cn", 1000, "/wss");

  // event handler
  webSocket.onEvent(webSocketEvent);

  // use HTTP Basic Authorization this is optional remove if not needed
  webSocket.setAuthorization("user=dapeng&password=efghijk&room=root");

  // try ever 5000 again if connection has failed
  webSocket.setReconnectInterval(500);

  // start heartbeat (optional)
  // ping server every 15000 ms
  // expect pong from server within 3000 ms
  // consider connection disconnected if pong is not received 2 times
  webSocket.enableHeartbeat(5000, 3000, 6);

}
void loop(void) {
  webSocket.loop();
}
void set_wifi() {
  ssid = ESP8266webServer.arg(0);
  passwd = ESP8266webServer.arg(1);
  String sp = ssid + "&" + passwd;
  Serial.println(sp);
  Serial.begin(19200);
  delay(500);
  WiFi.begin(ssid, passwd);
  WiFi.mode(WIFI_STA);
  delay(200);
  Serial.println("wifi_connet");
  int ii = 0;
  for (ii; ii < 30; ii++) {
    delay(500);
    Serial.print(".");
    if (WiFi.status() == WL_CONNECTED) {
      ESP8266webServer.close();
      delay(500);
      WiFi.disconnect();
      delay(500);
      //写入数据
      SPIFFS.format();
      delay(500);
      SPIFFS.begin();
      File dataFile = SPIFFS.open(flash_wifi, "w");
      dataFile.println(sp);
      dataFile.close();
      Serial.println("HTML连接成功");
      delay(1000);
      ESP.reset();
    }
  }

}
//------- 展示页面-------
void handleRoot() {
  ESP8266webServer.send(300, "text / html", html);
}
void handleNotFound(void) {
  ESP8266webServer.send(404, "text / plain", "404 - The page is not Found!");
}


void ap_start() {
  Serial.begin(19200);
  WiFi.mode(WIFI_AP);
  IPAddress local_IP(192, 168, 168, 2);
  IPAddress gateway(192, 168, 168, 1);
  IPAddress subnet(255, 255, 255, 0);
  WiFi.softAPConfig(local_IP, gateway, subnet) ;

  //启动AP模式，并设置账号和密码
  boolean result = WiFi.softAP("192.168.168.2", "192.168.168.2");
  if (result) {
    //输出 soft-ap ip地址
    //Serial.println(String("Soft - AP IP address = ") + WiFi.softAPIP());
  }
}
void webserver_start() {
  //-----------------启动webserver网络服务--------------------
  ESP8266webServer.begin(); // 启用网络服务
  ESP8266webServer.on(" / ", handleRoot);   // / 在HTTP中，表示请求首页服务，
  ESP8266webServer.on(" / set_wifi", set_wifi);
  ESP8266webServer.onNotFound(handleNotFound);    // 当服务器没找到所需的服务，调用404函数
  Serial.println("webserver started");
}
bool wifi_bool() {
  Serial.begin(19200);
  delay(500);
  //是否存在数据
  SPIFFS.begin();
  if (SPIFFS.exists(flash_wifi) == false) {
    Serial.print(flash_wifi);
    Serial.println(" NOT FOUND");
    return false;
  }

  File dateFile = SPIFFS.open(flash_wifi, "r");
  String result;
  for (int i = 0; i < dateFile.size(); i++) {
    //Serial.print((char)dateFile.read());
    result += (char)dateFile.read();
  }
  //去除前后空格
  result.trim();
  Serial.println(result);
  int i = result.indexOf("&");
  String wifiname = result.substring(0, i);
  String wifipw = result.substring(i + 1);
  Serial.println(wifiname);
  Serial.println(wifipw);
  WiFi.begin(wifiname, wifipw);
  delay(500);
  WiFi.mode(WIFI_STA);
  Serial.println("wifi_bool->connet");
  int ii = 0;
  for (ii; ii < 300; ii++) {
    delay(500);
    Serial.print(" . ");
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("wifi_bool 闪存配置连接成功");
      return true;
    }
  }
  Serial.println("wifi_bool->false");
  return false;
}

void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
  switch (type) {
    case WStype_DISCONNECTED:
      Serial.printf("[WSc] Disconnected!\n");
      break;
    case WStype_CONNECTED: {
        Serial.printf("[WSc] Connected to url: % s\n", payload);
        // send message to server when Connected
        webSocket.sendTXT("Connected");
      }
      break;
    case WStype_TEXT:
      Serial.printf("[WSc] get text: % s\n", payload);
      str = (const char*)payload;
      deserializeJson(jsonBuffer, str);
      root = jsonBuffer.as<JsonObject>();
      if (root["message"] != "") {
        String user = root["user"];
        String date = root["date"];
        String message = root["message"];
        if (message == "dapeng->d1开") {
          //关
          webSocket.sendTXT("d1开,通电正常 ...");
          digitalWrite(d1, HIGH);

          UserExec = user + " -> open d1 " + date;
        }
        if (message == "dapeng->d1关") {
          //关
          webSocket.sendTXT("d1关 断电正常...");
          digitalWrite(d1, LOW);
          UserExec = user + " -> close d1 " + date;
        }
        if (message == "AllState") {
          delay(1000);
          //查看设备是否在线，应做出回应
          if (UserExec == "") {
            webSocket.sendTXT("DS18B20 | 温湿度 | 加温 | 散温 | d6开 | d6关 | d1开 | d1关 | d1-open-s秒数 d1-close-s 支持d1和d6|参数 | show |reboot");
          } else {
            webSocket.sendTXT(UserExec);
          }
        }
        if (message == "dapeng->show") {
          if (show == false) {
            show = true;
          } else {
            show = false;
          }
        }
        if (message == "dapeng->ds18b20") {
          sensors.requestTemperatures(); // 发送命令获取温度
          //浮点转换成字符串
          float fb20 = sensors.getTempCByIndex(0);
          //显示温度
          //Serial.println( DHTt);
          String b20 = "ds18b20温度" + String(fb20);
          webSocket.sendTXT(b20);
        }

        if (message == "dapeng->散温") {
          temperature = 0;
          digitalWrite(d5, LOW);
          delay(500);
          digitalWrite(d2, HIGH);
          delay(500);
          webSocket.sendTXT("环境散温中...");
        }
        if (message == "dapeng->加温") {
          temperature = 1;
          digitalWrite(d2, LOW);
          delay(500);
          digitalWrite(d5, HIGH);
          delay(500);
          webSocket.sendTXT("环境加温中...");
        }

        if (message == "dapeng->d6开") {
          digitalWrite(d6, HIGH);
          delay(500);
          webSocket.sendTXT("d6开 通电正常...");
          UserExec = user + " -> open d6 " + date;
        }
        if (message == "dapeng->d6关") {
          digitalWrite(d6, LOW);
          delay(500);
          webSocket.sendTXT("d6关 断电正常...");
          UserExec = user + " -> close d6 " + date;
        }
        if (message == "dapeng->温湿度") {
          // 获取温度事件并打印其值.
          sensors_event_t event;
          dht.temperature().getEvent(&event);
          if (!isnan(event.temperature)) {
            DHTt = event.temperature;
          }
          dht.humidity().getEvent(&event);
          if (!isnan(event.relative_humidity)) {
            DHTh = event.relative_humidity;
          }
          webSocket.sendTXT("环境温度" + String(DHTt) + "°C 湿度" + String(DHTh) + "%");
        }

        // D6 秒钟单位 关闭
        if (message.indexOf("dapeng->d6-close-s") != -1) {
          int alllen = message.length();
          String re = message.substring(19);
          int res = re.toInt();
          myTicker.once(res, closed6);
          webSocket.sendTXT("d6定时关 设置成功 " + re + "秒");
        }
        if (message.indexOf("dapeng->d6-open-s") != -1) {
          int alllen = message.length();
          String re = message.substring(18);
          int res = re.toInt();
          myTicker.once(res, opend6);
          webSocket.sendTXT("d6定时开 设置成功 " + re + "秒");
        }
        // D1 秒钟单位 关闭
        if (message.indexOf("dapeng->d1-close-s") != -1) {
          int alllen = message.length();
          String re = message.substring(19);
          int res = re.toInt();
          myTicker.once(res, closed1);
          webSocket.sendTXT("d1定时关 设置成功 " + re + "秒");
        }
        if (message.indexOf("dapeng->d1-open-s") != -1) {
          int alllen = message.length();
          String re = message.substring(18);
          int res = re.toInt();
          myTicker.once(res, opend1);
          webSocket.sendTXT("d1定时开 设置成功 " + re + "秒");
        }

        if (message == "dapeng->参数") {
          webSocket.sendTXT("参数 温度大于" + String(DHTtMax) + "散温 温度小于" + String(DHTtMin) + "加温 DS18B20 大于" + String(DS18B20) + "关闭d6");
        }
        if (message == "dapeng->reboot") {
          ESP.reset();
        }
      }
      // send message to server
      //       webSocket.sendTXT("message here");
      break;
    case WStype_BIN:
      Serial.printf("[WSc] get binary length: % u\n", length);
      hexdump(payload, length);

      // send data to server
      // webSocket.sendBIN(payload, length);
      break;
    case WStype_PING:
      // pong will be send automatically
      Serial.printf("[WSc] get ping\n");
      break;
    case WStype_PONG:
      // answer to a ping we send
      sensors_event_t event;
      dht.temperature().getEvent(&event);

      DHTt = event.temperature;
      dht.humidity().getEvent(&event);
      if (isnan(event.relative_humidity)) {
        i++;
        if (i > 10) {
          i=0;
          http_get("http://www.360xyz.cn/automation/SendToEmail?message=DHT传感器损坏");
        }
      }else{
        i=0;
      }
      DHTh = event.relative_humidity;
      i = 0;
      //刚启动 温湿度会显示一样
      if ((DHTt == DHTh) && (DHTt > 50)) {
        Serial.println("刚启动");
      } else {
        //散温
        if ((DHTt > DHTtMax) && (temperature != 1)) {
          //这个值的作用是如果是开启状态,只运行一次开启
          temperature = 1;
          //大于大棚设置温度就启动散温
          digitalWrite(d2, HIGH);
          delay(500);
          digitalWrite(d5, LOW);
          delay(500);
          http_get("http://www.360xyz.cn/automation/SendToEmail?message=散温中..." + String(DHTt) + "°C 湿度" + String(DHTh) + "%");
          webSocket.sendTXT("散温中...温度" + String(DHTt) + "°C 湿度" + String(DHTh) + "%");

        }
        //加温
        if ((DHTt < DHTtMin) && (temperature != 0)) {
          temperature = 0;
          digitalWrite(d2, LOW);
          delay(300);
          digitalWrite(d5, HIGH);
          delay(300);
          webSocket.sendTXT("加温中...温度" + String(DHTt) + "°C 湿度" + String(DHTh) + "%");
          delay(300);
          http_get("http://www.360xyz.cn/automation/SendToEmail?message=加温中..." + String(DHTt) + "°C 湿度" + String(DHTh) + "%");

        }
        //不加温和不散温状态
        if ((DHTt < DHTtMax) && (DHTt > DHTtMin) && (temperature != 2)) {
          temperature = 2;
          digitalWrite(d2,  LOW);
          delay(200);
          digitalWrite(d5, LOW);
          delay(200);
          webSocket.sendTXT("最佳温湿度无状态...温度" + String(DHTt) + "°C 湿度" + String(DHTh) + "%");
          delay(300);
          http_get("http://www.360xyz.cn/automation/SendToEmail?message=最佳温湿度无状态..." + String(DHTt) + "°C 湿度" + String(DHTh) + "%");
        }
        if ((DHTh > DHThMax) || (DHTh < DHThMin) && (temperature != 3) ) {
          temperature = 3;
          webSocket.sendTXT("邮件提醒...温度" + String(DHTt) + "°C 湿度" + String(DHTh) + "%");
          delay(300);
          http_get("http://www.360xyz.cn/automation/SendToEmail?message=湿度异常...温度" + String(DHTt) + "°C 湿度" + String(DHTh) + "%");

        }
      }


      sensors.requestTemperatures(); // 发送命令获取温度
      //浮点转换成字符串
      float fb20 = sensors.getTempCByIndex(0);
      if (fb20 > DS18B20) {
        digitalWrite(d6, LOW);
        delay(500);
        webSocket.sendTXT("DS18B20温度" + String(fb20) + "大于" + String(DS18B20) + " d6关 断电正常");
      }
      if (show == true) {
        webSocket.sendTXT("环境温度" + String(DHTt) + "湿度" + String(DHTh) + "%");
      }

      Serial.printf("[WSc] get pong\n");
      break;
  }
}
String http_get(String string) {
  HTTPClient http;
  String response;
  http.begin(string);
  http.setTimeout(5000);
  int httpCode = http.GET();
  if (httpCode == 200) {
    //读取响应内容
    response = http.getString();
    //Serial.println(response);
    return response;
  }
  return "false";
  http.end();
}
void closed6() {
  Serial.println("d6定时触发 关");
  digitalWrite(d6, LOW);
  delay(500);
  //取消定时状态
  myTicker.detach();
  delay(500);
  http_get("http://www.360xyz.cn/internet/send_email?title=d6定时触发 关 dapeng->d6-close-i|-s");
}
void opend6() {
  Serial.println("d6定时触发 开");
  digitalWrite(d6, HIGH);
  delay(500);
  //取消定时状态
  myTicker.detach();
  delay(500);
  http_get("http://www.360xyz.cn/internet/send_email?title=d6定时触发 开 dapeng->d6-close-i|-s");
}
void closed1() {
  Serial.println("d1定时触发 关");
  digitalWrite(d1, LOW);
  delay(500);
  //取消定时状态
  myTicker.detach();
  delay(500);
  http_get("http://www.360xyz.cn/internet/send_email?title=d1定时触发 关 dapeng->d1-close-i|-s");
}
void opend1() {
  Serial.println("d1定时触发 开");
  digitalWrite(d1, HIGH);
  delay(500);
  //取消定时状态
  myTicker.detach();
  delay(500);
  http_get("http://www.360xyz.cn/internet/send_email?title=d1定时触发 开 dapeng->d1-close-i|-s");
}