實驗自己動手做智能插座,以Mosquitto MQTT Broker與Node-RED控制智能插座,安裝Node-RED Dashboard,因為Dashboard的UI-Template可以帶html與Angular/Angular-Material指令(The template widget can contain any valid html and Angular/Angular-Material directives),因此本實驗UI部分,僅使用Node-RED Dashboard中的UI-Template元件,不用事先規劃多少智能插座的畫面,讓不限數量的智能插座連上來,動態全部顯示在面板上。資料庫部分使用SQLite3,並安裝node-red-node-sqlite存取SQLite資料庫檔案。
功能需求如下:
智能插座的功能:
- 遠端控制電源開與關
- 電源開與關倒數計時
- 外出不在家時,在指定的時段設定隨機開關時間
- 電源開關時間排程
一、Node-RED(server side)與智能插座(client side) Topic Message Flows:
伺服端使用Raspberry Pi 3,安裝mosquitto MQTT Broker, Node-RED,SQLite3。設備端使用ESP8266(ESP-01S)與繼電器模組搭配電工插座製作一個智能插座。為了網路安全,通訊使用TLS/SSL協定,設備端與MQTT端的Topic為兩大類:Network State與network socket, Smart Socket devices與Node-RED之間的Topic flow與端點的動作如下圖所示。
本實驗將不限制連上多少智能插座,且每個智能插座將獨立顯示一個獨立UI元件(狀態顯示與按鈕功能),所以主面板上的元件個數將是動態顯示,當有新的元件連上時,主動增加一個UI元件,如下圖所示,原來四個,新增一個後主動更新面板顯示個數。四、完成影片展示
因為Node-RED Dashboard UI-Template支援html與Angular/Angular-Material,因此本實驗將以UI-Template為主要元件來動態顯示元件個數。
如上圖所示,UI-Template以watch function接收其他node送進來的msg,以send指令送出msg,搭配html標記完成完成整個Template。Dashboard UI-Template與Node-RED其他nodes之間flows設計如下圖所示:
- 完整Flows Code請參閱文章最後面說明。
三、智能插座端
智能插座端使用ESP8266(ESP-01S)搭配繼電器模組與電工材料組成一個智能插座,
ESP8266 WiFi工作模式以AP mode與Station Mode之間切換,不採用WIFI_AP_STA模式。AP Mode主要為設定參數用,包括AP passwd,SSID連線設定與MQTT broker連線參數,為網路安全起見,client端連線採用TLS/SSL,並取需要Client CERT與Server CERT的fingerprint。
四、完成影片展示
五、智能插座端程式碼
SmartSocket.ino( main code)
#include <ESP8266WiFi.h> #include <PubSubClient.h> #include <ArduinoJson.h> #include <FS.h> #include <ESP8266WebServer.h> #include "webhtml.h" unsigned long mqttConnTm = 0; unsigned long WiFiClientConnTm = 0; String mac_suffix = ""; #define _DEBUG ESP8266WebServer server(80); //// loda initial configuration data void loadData() { char *buff; bWebPassLoaded = false; bMQTTServerAttrib = true; SPIFFS.begin(); File fs; fs = SPIFFS.open("passdat.dat", "r"); if (fs) { webpass = ""; while (fs.available()) { webpass = webpass + (char)fs.read(); } fs.close(); } else { webpass = "adminxxx"; } bWebPassLoaded = true; #ifdef _DEBUG Serial.printf("Web Pass loaded:%d\n", bWebPassLoaded); #endif // load Client Cert fs = SPIFFS.open("/client.crt", "r"); if (!fs) { #ifdef _DEBUG Serial.println("Couldn't load Client Cert"); #endif bMQTTServerAttrib = false; } else if (fs.size() > 0) { size_t certSize = fs.size(); buff = (char *)malloc(certSize); if (certSize != fs.readBytes(buff, certSize)) { #ifdef _DEBUG Serial.println("Loading Client cert failed"); #endif free(buff); bMQTTServerAttrib = false; } else { #ifdef _DEBUG //delay(2000); Serial.println("Loaded Client cert"); #endif clientCert = new BearSSL::X509List(buff); } free(buff); fs.close(); } else { bMQTTServerAttrib = false; } // load Client PrivateKey fs = SPIFFS.open("/client.key", "r"); if (!fs) { #ifdef _DEBUG Serial.println("Couldn't load Client Key"); #endif bMQTTServerAttrib = false; } else if (fs.size() > 0) { size_t tsize = fs.size(); buff = (char *)malloc(tsize); if (tsize != fs.readBytes(buff, tsize)) { #ifdef _DEBUG Serial.println("Loading Client PrivateKey failed"); #endif free(buff); bMQTTServerAttrib = false; } else { #ifdef _DEBUG Serial.println("Loaded Client Private Key"); #endif clientPrivateKey = new BearSSL::PrivateKey(buff); } free(buff); fs.close(); } else { bMQTTServerAttrib = false; } // load josn data fs = SPIFFS.open("/configu.dat", "r"); if (!fs) { #ifdef _DEBUG Serial.println("Couldn't configuration data"); #endif bMQTTServerAttrib = false; } else if (fs.size() > 0) { size_t tsize = fs.size(); buff = (char *)malloc(tsize); if (tsize != fs.readBytes(buff, tsize)) { #ifdef _DEBUG Serial.println("Loading configuration data"); #endif free(buff); bMQTTServerAttrib = false; } else { #ifdef _DEBUG Serial.println("Loaded configuration data"); #endif } fs.close(); DynamicJsonDocument doc(1024); DeserializationError error = deserializeJson(doc, buff); if (error) { #ifdef _DEBUG Serial.println("Json parse error"); #endif free(buff); bMQTTServerAttrib = false; } #ifdef _DEBUG serializeJson(doc, Serial); #endif if (doc.containsKey("devicename")) { devicename = doc["devicename"].as<String>(); } if (doc.containsKey("username")) { const char* tusername = doc["username"].as<const char*>(); username = (char *)malloc(strlen(tusername)); strcpy(username, tusername); } if (doc.containsKey("password")) { const char* tpassword = doc["password"].as<const char*>(); password = (char *)malloc(strlen(tpassword)); strcpy(password, tpassword); } if (doc.containsKey("ssid")) { const char* tssid = doc["ssid"].as<const char*>(); ssid = (char *)malloc(strlen(tssid)); strcpy(ssid, tssid); } if (doc.containsKey("wlpass")) { const char* twlpass = doc["wlpass"].as<const char*>(); wlpass = (char *)malloc(strlen(twlpass)); strcpy(wlpass, twlpass); } if (doc.containsKey("fingerprint")) { const char* tmqttCertFingerprint = doc["fingerprint"].as<const char*>(); mqttCertFingerprint = (char *)malloc(strlen(tmqttCertFingerprint)); strcpy(mqttCertFingerprint, tmqttCertFingerprint); } if (doc.containsKey("mqttServer")) { const char* tmqttServer = doc["mqttServer"].as<const char*>(); mqttServer = (char *)malloc(strlen(tmqttServer)); strcpy(mqttServer, tmqttServer); } if (doc.containsKey("mqttPort")) { int tmqttPort = doc["mqttPort"].as<int>(); mqttPort = tmqttPort; } free(buff); } else { bMQTTServerAttrib = false; } } // softAP and webserver bool startSoftAPServer() { int tryCount = 0; bWiFiSoftAP = false; #ifdef _DEBUG Serial.println("Start SoftAP..."); #endif if (!bWebPassLoaded) return false; //WiFi.mode(WIFI_AP_STA); WiFi.mode(WIFI_AP); #ifdef _DEBUG Serial.println(mac_suffix); #endif String ap = "ESP-" + mac_suffix; if (WiFi.softAP(ap.c_str(), webpass.c_str())) { bWiFiSoftAP = true; } else { #ifdef _DEBUG Serial.println("SoftAP Error"); #endif return false; } #ifdef _DEBUG IPAddress myIP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(myIP); #endif // initial web server server.on("/", handleRoot); server.on("/login", handleLogin); server.on("/logout", handleLogout); server.on("/chpass", handleChpass); server.on("/upload", HTTP_POST, handleAfterUpload, handleUpload); server.onNotFound(handleNotFound); //ask server to track these headers server.collectHeaders("User-Agent", "Cookie"); server.begin(); #ifdef _DEBUG Serial.println("HTTP server started"); #endif return true; } void setCurrentTime() { #ifdef _DEBUG Serial.print("Waiting for NTP time sync "); #endif time_t now = time(nullptr); while (now < 8 * 3600 * 2) { delay(500); #ifdef _DEBUG Serial.print("."); #endif now = time(nullptr); } struct tm timeinfo; gmtime_r(&now, &timeinfo); #ifdef _DEBUG Serial.println(""); Serial.print("Current time: "); Serial.print(asctime(&timeinfo)); #endif } bool connectToMQTT() { if (!bMQTTServerAttrib) return false; /* Configure secure client connection */ //espClient.setTrustAnchors(&caCertX509); /* Load CA cert into trust store */ espClient.allowSelfSignedCerts(); /* Enable self-signed cert support */ espClient.setFingerprint(mqttCertFingerprint); /* Load SHA1 mqtt cert fingerprint for connection validation */ espClient.setClientRSACert(clientCert, clientPrivateKey); #ifdef _DEBUG Serial.printf("Connect to MQTT server: %s\n", mqttServer); #endif TOPIC = TOPIC_PREFIX + "/Network/State"; char msg[256] = ""; //String payload = "{\"ID\":\""+mqttClientID.c_str()+"\",\"Status\":\"OFFLINE\"}"; sprintf(msg, "{\"ID\":\"%s\",\"State\":\"OFFLINE\"}", mqttClientID.c_str()); if (!mqttClient.connected()) { if (mqttClient.connect(mqttClientID.c_str(), username, password, TOPIC.c_str(), 1, false, msg)) { // WillMessage:OFFICE :qos=1 sprintf(msg, "{\"ID\":\"%s\",\"Devicename\":\"%s\",\"State\":\"CONNECTED\"}", mqttClientID.c_str(), devicename.c_str()); mqttClient.publish(TOPIC.c_str(), msg); //digitalWrite(RED_LED_PIN, LOW); digitalWrite(GREEN_LED_PIN, HIGH); //// subscribe Topics when connect to server successfully //// TOPIC = TOPIC_PREFIX + "/Socket/CMD"; mqttClient.subscribe(TOPIC.c_str(), 1); /////////////////////////////// } else { #ifdef _DEBUG Serial.printf("Connect to MQTT server error\n"); #endif return false; } } #ifdef _DEBUG Serial.printf("MQTT server Connected\n"); #endif return true; } // callback function void mqttSubCallback(char* topic, byte* payload, unsigned int length) { String rcvPayload = (char*)payload; rcvPayload = rcvPayload.substring(0, length); TOPIC = topic; // case 1: switch CMD ================ if (TOPIC.startsWith(TOPIC_PREFIX) && TOPIC.endsWith("Socket/CMD")) { if (rcvPayload == "ON") { digitalWrite(RELAY_PIN, LOW); } if (rcvPayload == "OFF") { digitalWrite(RELAY_PIN, HIGH); } //======= enter AP mode================= #ifdef _DEBUG Serial.println(rcvPayload); #endif if (rcvPayload == "APMODE") { WiFi.disconnect(); bWiFiClient = false; WiFi.mode(WIFI_AP); WiFi.reconnect(); #ifdef _DEBUG Serial.println("enter AP Mode"); #endif startSoftAPServer(); return; } //======= network disconnect TOPIC = TOPIC_PREFIX + "/Socket/State"; rcvPayload = "{\"ID\":\"" + mqttClientID + "\",\"State\":\"" + rcvPayload + "\"}"; mqttClient.publish(TOPIC.c_str(), rcvPayload.c_str()); } // case 2: ========================= } int pc = 0; void reportStateFunc() { char buf[100]; sprintf(buf, "This is from polling function:%d", pc++); mqttClient.publish("test", buf); } // connect to WiFi network bool connectToWiFi() { int try_cnt = 0; bWiFiClient = false; // connect WiFi Client if (!bMQTTServerAttrib) { startSoftAPServer(); return false; } WiFi.mode(WIFI_STA); WiFi.begin(ssid, wlpass); #ifdef _DEBUG Serial.printf("Connect to %s", ssid); #endif while (WiFi.status() != WL_CONNECTED && try_cnt < 120) { // must connect to WiFi in 1 minute. #ifdef _DEBUG Serial.print("."); #endif try_cnt++; delay(500); } if (try_cnt >= 120 && WiFi.status() != WL_CONNECTED) { WiFi.disconnect(); WiFi.mode(WIFI_AP); WiFi.reconnect(); #ifdef _DEBUG Serial.println("Change to AP mode"); #endif startSoftAPServer(); return false; } bWiFiClient = true; mqttClientID = "ESPSS_" + mac_suffix; #ifdef _DEBUG Serial.println(); Serial.println("connect WiFi successfully..."); Serial.println(mqttClientID); #endif TOPIC_PREFIX = "SmartSocket/" + mqttClientID; #ifdef _DEBUG Serial.printf("\nConnected: \nlocalIP=%s, mqttClinetID=%s\n", WiFi.localIP().toString().c_str(), mqttClientID.c_str()); Serial.println("Connect WiFi OK"); #endif setCurrentTime(); mqttClient.setKeepAlive(15); return true; } bool setupOK = false; // Is initial setup steps OK? ////////////// setup function //////////////////// void setup() { pinMode(GREEN_LED_PIN, OUTPUT); //pinMode(RED_LED_PIN, OUTPUT); pinMode(RELAY_PIN, OUTPUT); //digitalWrite(RED_LED_PIN, HIGH); digitalWrite(GREEN_LED_PIN, LOW); digitalWrite(RELAY_PIN, HIGH); //ESP-01S, RELAY_PIN IO0 LOW: NC, HIGH: NO #ifdef _DEBUG Serial.begin(115200); #endif loadData(); mac_suffix = WiFi.macAddress(); mac_suffix.replace(":", ""); mac_suffix = mac_suffix.substring(6); #ifdef _DEBUG Serial.println(); Serial.println("Load Data "); #endif if (!connectToWiFi()) return; //ESP.restart(); setupOK = true; } int mqttTry = 0; void loop() { //if (setupOK) { if (bWiFiClient) { if (!mqttClient.connected()) { unsigned long now_t = millis(); if (now_t - mqttConnTm > 10000) { mqttConnTm = now_t; mqttTry++; if (mqttTry > 12) { WiFi.disconnect(); bWiFiClient = false; // enter to AP mode to correct config data WiFi.mode(WIFI_AP); WiFi.reconnect(); startSoftAPServer(); } else { connectToMQTT(); } } else if (now_t < mqttConnTm) { mqttConnTm = now_t; } } else { mqttClient.loop(); } } else if (!bWiFiSoftAP) { unsigned long now_t = millis(); if (now_t - WiFiClientConnTm > 1000) { WiFiClientConnTm = now_t; connectToWiFi(); } else if (now_t < WiFiClientConnTm) { WiFiClientConnTm = now_t; } } else if (bWebPassLoaded && bWiFiSoftAP) server.handleClient(); }webpage.ino (AP mode as web server functions)
//Check if header is present and correct bool is_authenticated() { #ifdef _DEBUG Serial.println("Enter is_authenticated"); #endif if (server.hasHeader("Cookie")) { #ifdef _DEBUG Serial.print("Found cookie: "); #endif String cookie = server.header("Cookie"); #ifdef _DEBUG Serial.println(cookie); #endif if (cookie.indexOf("ESPSESSIONID=1") != -1) { #ifdef _DEBUG Serial.println("Authentication Successful"); #endif return true; } } #ifdef _DEBUG Serial.println("Authentication Failed"); #endif return false; } // if no file upload set to false //login page, also called for disconnect void handleLogin() { String msg; if (server.hasHeader("Cookie")) { #ifdef _DEBUG Serial.print("Found cookie: "); #endif String cookie = server.header("Cookie"); #ifdef _DEBUG Serial.println(cookie); #endif } if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) { if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == webpass) { server.sendHeader("Location", "/"); server.sendHeader("Cache-Control", "no-cache"); server.sendHeader("Set-Cookie", "ESPSESSIONID=1"); server.send(301); #ifdef _DEBUG Serial.println("Log in Successful"); #endif return; } msg = "Wrong username/password! try again."; #ifdef _DEBUG Serial.println("Log in Failed"); #endif } String content = loginhtml; server.send(200, "text/html", content); } //root page can be accessed only if authentication is ok void handleRoot() { #ifdef _DEBUG Serial.println("Enter handleRoot"); #endif String header; if (!is_authenticated()) { server.sendHeader("Location", "/login"); server.sendHeader("Cache-Control", "no-cache"); server.send(301); return; } // set current attributes String content = roothtml; content.replace("{{devicename}}", devicename); content.replace("{{ssid}}", ssid); content.replace("{{wlpass}}", wlpass); content.replace("{{mqttCertFingerprint}}", mqttCertFingerprint); content.replace("{{username}}", username); content.replace("{{password}}", password); content.replace("{{mqttServer}}", mqttServer); content.replace("{{mqttPort}}", String(mqttPort)); server.send(200, "text/html", content); } //logout clean session cookie void handleLogout() { server.sendHeader("Location", "/login"); server.sendHeader("Cache-Control", "no-cache"); server.sendHeader("Set-Cookie", "ESPSESSIONID=0"); server.send(301); } //change web admin password void handleChpass() { if (!is_authenticated()) { server.sendHeader("Location", "/login"); server.sendHeader("Cache-Control", "no-cache"); server.send(301); return; } if (!server.hasArg("newpass") || !server.hasArg("confirmnewpass")) { server.send(200, "text/html", chpasspage); } else { if (server.arg("newpass") != server.arg("confirmnewpass")) { String msg = (String)chpasspage + (String)"<p align='center'>two password not match</p>"; server.send(200, "text/html", msg); } else { File fs = SPIFFS.open("/conf/pass.dat", "w"); if (fs) { fs.print(server.arg("newpass")); fs.close(); webpass = server.arg("newpass"); // reboot device String msg = "<meta http-equiv='refresh' content='10; url=/login' />"; server.sendHeader("Cache-Control", "no-cache"); server.sendHeader("Set-Cookie", "ESPSESSIONID=0"); server.send(301, "text/html", msg + "Password changed. Reconnect SoftAP after rebooted.<br><br>Device is rebooting..."); delay(2000); ESP.restart(); //handleLogout(); return; } else { server.send(200, "text/plain", "unknow error"); } } } } //no need authentication void handleNotFound() { String message = "File Not Found\n\n"; server.send(404, "text/plain", message); } bool bFileUpload = true; File uploadfile; void handleUpload() { HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { if (upload.name == "clientCert") uploadfile = SPIFFS.open("/conf/client.crt", "w"); if (upload.name == "clientPK") uploadfile = SPIFFS.open("/conf/client.key", "w"); } if (!uploadfile) bFileUpload = false; if (upload.status == UPLOAD_FILE_WRITE) { if (uploadfile) { uploadfile.write(upload.buf, upload.currentSize); } } if (upload.status == UPLOAD_FILE_END) { if (uploadfile) uploadfile.close(); if (upload.totalSize == 0) bFileUpload = false; } } void handleAfterUpload() { if (!bFileUpload) { String msg = "<meta http-equiv='refresh' content='3; url=/' />"; server.send(301, "text/html", msg + "<p align='center'><font color='RED' SIZE='5'>upload file size is zero</font></p>"); bFileUpload = true; return; } String conf = "{\"devicename\":\"" + server.arg("devicename") + "\",\"ssid\":\"" + server.arg("ssid") + "\",\"wlpass\":\"" + server.arg("password") + "\",\"fingerprint\":\"" + \ server.arg("serverFP") + "\",\"username\":\"" + server.arg("mqttUsername") + "\",\"password\":\"" + server.arg("mqttPassword") + "\",\"mqttServer\":\"" + server.arg("mqttServer") + "\",\"mqttPort\":" + server.arg("mqttPort") + "}"; #ifdef _DEBUG Serial.println(conf); #endif File fp = SPIFFS.open("/conf/conf.dat", "w"); if (fp) { fp.print(conf); fp.close(); } String msg = "<meta http-equiv='refresh' content='10; url=/login' />"; server.sendHeader("Cache-Control", "no-cache"); server.sendHeader("Set-Cookie", "ESPSESSIONID=0"); server.send(301, "text/html", msg + "Configuration is changed. Device is rebooting..."); delay(2000); ESP.restart(); }webhtml.h (web page)
#define GREEN_LED_PIN 2 #define RED_LED_PIN 1 #define RELAY_PIN 0 bool bWebPassLoaded=false; //is webPass loaded from file bool bWiFiSoftAP=false; bool bWiFiClient=false; bool bMQTTServerAttrib=false; //is MQTT Server connection attrib loaded String webpass="adminxxx"; // default web server password String devicename=""; char *ssid="", *wlpass=""; char *mqttCertFingerprint=""; char *username="", *password=""; char* mqttServer = "mqttbroker.local"; int mqttPort = 8883; String mqttClientID = "ESPSS_"; String TOPIC_PREFIX="SmartSocket/cid"; String TOPIC; void mqttSubCallback(char* topic, byte* payload, unsigned int length); String wifiMACLast6; WiFiClientSecure espClient; PubSubClient mqttClient(mqttServer, mqttPort, mqttSubCallback, espClient); X509List *clientCert; /* X.509 parsed CA Cert */ PrivateKey *clientPrivateKey; const char roothtml[] PROGMEM = R"HTML( <html> <head> <title>ESP8266 Smart Socket configuration</title> <meta charset="utf-8"> </head> <body> <form method="post" enctype="multipart/form-data" action="upload"> <table style="text-align: left; margin-left: auto; margin-right: auto;" border="0" cellpadding="2" cellspacing="2"> <tbody> <tr> <td colspan="1" style="text-align: right;">Device Name:</td> <td colspan="3" style="text-align: left;"><input name="devicename" type="text" value="{{devicename}}"></td> </tr> <tr> <td colspan="4" style="text-align: left;">WiFi:</td> </tr> <tr> <td style="text-align: right; width:10%;"> SSID:</td> <td style="align: left;"><input name="ssid" type="text" value="{{ssid}}"></td> <td style="text-align: right; width:10%;">Password:</td> <td style="align: left;"><input name="password" type="password" value="{{wlpass}}"></td> </tr> <tr> <td colspan="4" style="text-align: left;">MQTT:</td> </tr> <tr> <td style="text-align: right; width:10%;"> Server:</td> <td style="align: left;"><input name="mqttServer" type="text" value="{{mqttServer}}"></td> <td style="text-align: right; width:10%;">Port:</td> <td style="align: left;"><input name="mqttPort" type="text" value="{{mqttPort}}"></td> </tr> <tr> <td style="text-align: right; width:10%;"> Username: </td> <td style="align: left;"><input name="mqttUsername" type="text" value="{{username}}"></td> <td style="text-align: right; width:10%;">Password:</td> <td style="align: left;"><input name="mqttPassword" type="password" value="{{password}}"></td> </tr> </tbody> </table> <table style="text-align: left; margin-left: auto; margin-right: auto;" border="0" cellpadding="2" cellspacing="2"> <tbody> <tr> <td colspan="2" style="text-align: left;">Files:</td> </tr> <tr> <td style="text-align: right; width:25%;"> Server CERT fingerprint:</td> <td style="align: left;"><input name="serverFP" type="text" value="{{mqttCertFingerprint}}"> </td> </tr> <tr> <td style="text-align: right;width:25%;">Client CERT: </td> <td style="align: left;"><input name="clientCert" type="file"> </td> </tr> <tr> <td style="text-align: right;width:25%;">Client Primary Key: </td> <td style="align: left;"><input name="clientPK" type="file"> </td> </tr> </tbody> </table> <p align="center"> <input class="button" value="Upload" type="submit" > </p> </form> <p align="center"><a href="/chpass">change login password</a> <a href="/logout">Logout</a></p> </body> </html> )HTML"; const char loginhtml[] PROGMEM = R"LOGIN( <html><body><form action='/login' method='POST'> <table style="text-align: left;margin-left: auto; margin-right: auto; " border="0" cellpadding="2" cellspacing="2"> <tbody> <tr> <td style="text-align: right;">User:</td> <td><input type='text' name='USERNAME' placeholder='user name'></td> </tr> <tr> <td style="text-align: right;">Password:</td> <td><input type='password' name='PASSWORD' placeholder='password'></td> </tr> <tr><td colspan="2"><p align="center"><input type='submit' name='SUBMIT' value='Submit'></p></td> </tbody> </table> </form> </body></html> )LOGIN"; char const chpasspage[] PROGMEM = R"CHPASS( <html><body><form action='/chpass' method='POST'> <p align="center">new password:<input type="password" name="newpass" minlength="8" ></p> <p align="center">confirm new password:<input type="password" name="confirmnewpass" minlength="8"></p> <p align="center"><input class="button" value="Upload" type="submit"></p> </form> </body></html> )CHPASS";
六、Node-RED flows
[ { "id": "1170da0f4e4c8045", "type": "tab", "label": "root flow", "disabled": false, "info": "" }, { "id": "30074137ff8b6e70", "type": "tab", "label": "new smart socket", "disabled": false, "info": "" }, { "id": "4bc53f77f2c857ba", "type": "tab", "label": "sockets pannel", "disabled": false, "info": "" }, { "id": "439e43cb848a5281", "type": "tab", "label": "settings", "disabled": false, "info": "" }, { "id": "de64abcc7738c08d", "type": "mqtt-broker", "name": "mosquitto", "broker": "localhost", "port": "1883", "clientid": "", "usetls": false, "protocolVersion": "4", "keepalive": "60", "cleansession": true, "birthTopic": "", "birthQos": "0", "birthPayload": "", "birthMsg": {}, "closeTopic": "", "closeQos": "0", "closePayload": "", "closeMsg": {}, "willTopic": "", "willQos": "0", "willPayload": "", "willMsg": {}, "sessionExpiry": "" }, { "id": "0c752464ee7109c3", "type": "sqlitedb", "db": "/home/db/smartSocket.db", "mode": "RWC" }, { "id": "160b9bec8625389b", "type": "ui_base", "theme": { "name": "theme-light", "lightTheme": { "default": "#0094CE", "baseColor": "#0094CE", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "edited": true, "reset": false }, "darkTheme": { "default": "#097479", "baseColor": "#097479", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "edited": false }, "customTheme": { "name": "Untitled Theme 1", "default": "#4B7930", "baseColor": "#4B7930", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" }, "themeState": { "base-color": { "default": "#0094CE", "value": "#0094CE", "edited": false }, "page-titlebar-backgroundColor": { "value": "#0094CE", "edited": false }, "page-backgroundColor": { "value": "#fafafa", "edited": false }, "page-sidebar-backgroundColor": { "value": "#ffffff", "edited": false }, "group-textColor": { "value": "#1bbfff", "edited": false }, "group-borderColor": { "value": "#ffffff", "edited": false }, "group-backgroundColor": { "value": "#ffffff", "edited": false }, "widget-textColor": { "value": "#111111", "edited": false }, "widget-backgroundColor": { "value": "#0094ce", "edited": false }, "widget-borderColor": { "value": "#ffffff", "edited": false }, "base-font": { "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" } }, "angularTheme": { "primary": "indigo", "accents": "blue", "warn": "red", "background": "grey", "palette": "light" } }, "site": { "name": "Node-RED Dashboard", "hideToolbar": "true", "allowSwipe": "false", "lockMenu": "false", "allowTempTheme": "true", "dateFormat": "DD/MM/YYYY", "sizes": { "sx": 48, "sy": 48, "gx": 6, "gy": 6, "cx": 6, "cy": 6, "px": 0, "py": 0 } } }, { "id": "87ba6f7258ac6e27", "type": "ui_tab", "name": "Home", "icon": "dashboard", "disabled": false, "hidden": true }, { "id": "fc6b92455ad3b35e", "type": "ui_group", "name": "Smart Sockets", "tab": "87ba6f7258ac6e27", "order": 1, "disp": true, "width": "14", "collapse": false, "className": "" }, { "id": "91acea068bb95a30", "type": "ui_tab", "name": "Settings", "icon": "settings", "disabled": false, "hidden": true }, { "id": "8a15ca423c637161", "type": "ui_group", "name": "Settings", "tab": "91acea068bb95a30", "order": 1, "disp": true, "width": "16", "collapse": false, "className": "" }, { "id": "b7998b096794fc09", "type": "mqtt in", "z": "1170da0f4e4c8045", "name": "", "topic": "SmartSocket/+/Network/State", "qos": "1", "datatype": "auto", "broker": "de64abcc7738c08d", "nl": false, "rap": true, "rh": 0, "x": 140, "y": 40, "wires": [ [ "fb4f1cbd4d446f98" ] ] }, { "id": "fb4f1cbd4d446f98", "type": "json", "z": "1170da0f4e4c8045", "name": "", "property": "payload", "action": "", "pretty": false, "x": 190, "y": 100, "wires": [ [ "8a9d09d425e6e505" ] ] }, { "id": "8a9d09d425e6e505", "type": "switch", "z": "1170da0f4e4c8045", "name": "", "property": "payload[\"State\"]", "propertyType": "msg", "rules": [ { "t": "eq", "v": "OFFLINE", "vt": "str" }, { "t": "eq", "v": "CONNECTED", "vt": "str" } ], "checkall": "true", "repair": false, "outputs": 2, "x": 310, "y": 100, "wires": [ [ "da5685310d73b444" ], [ "0a9e2d8884e966ab" ] ] }, { "id": "da5685310d73b444", "type": "function", "z": "1170da0f4e4c8045", "name": "set Device OFFLINE", "func": "msg.topic = \"update device set networkState=0 where deviceID='\"+msg.payload[\"ID\"]+\"'\";\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 500, "y": 60, "wires": [ [ "b830328e6b146c68" ] ] }, { "id": "0a9e2d8884e966ab", "type": "link out", "z": "1170da0f4e4c8045", "name": "is new SS?", "links": [ "000740d0e2456877" ], "x": 435, "y": 120, "wires": [] }, { "id": "a06c018939fd3ba8", "type": "comment", "z": "1170da0f4e4c8045", "name": "Is new Smart Socket?", "info": "", "x": 500, "y": 160, "wires": [] }, { "id": "51e113dbd267bd84", "type": "function", "z": "30074137ff8b6e70", "name": "update or insert new socket", "func": "if (msg.payload[0].rs > 0) {\n msg.topic = \"update device set networkState=1, deviceName='\"+msg.payload[0][\"deviceName\"]+\"' where deviceID='\"+msg.payload[0][\"deviceID\"]+\"'\";\n}\nelse {\n msg.topic=\"Insert into device(deviceID, deviceName, networkState) values('\"+msg.payload[0][\"deviceID\"]+\"','\"+msg.payload[0][\"deviceName\"]+\"',1)\";\n}\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 520, "y": 60, "wires": [ [ "66fd97fe8ba0ce7b" ] ] }, { "id": "372853647801eb2b", "type": "sqlite", "z": "30074137ff8b6e70", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "device is Exits?", "x": 300, "y": 60, "wires": [ [ "51e113dbd267bd84" ] ] }, { "id": "f041e68b229222da", "type": "function", "z": "30074137ff8b6e70", "name": "check device whether exist", "func": "msg.topic=\"select count(*) as rs, '\"+msg.payload[\"ID\"] +\"' as deviceID, '\"+msg.payload[\"Devicename\"]+\"' as deviceName from device where deviceID='\"+msg.payload[\"ID\"]+\"'\";\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 180, "y": 160, "wires": [ [ "372853647801eb2b" ] ] }, { "id": "000740d0e2456877", "type": "link in", "z": "30074137ff8b6e70", "name": "is new socket", "links": [ "0a9e2d8884e966ab" ], "x": 55, "y": 100, "wires": [ [ "f041e68b229222da" ] ] }, { "id": "66fd97fe8ba0ce7b", "type": "sqlite", "z": "30074137ff8b6e70", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "update database", "x": 510, "y": 140, "wires": [ [ "d2c2f0caf6bf039d" ] ] }, { "id": "b830328e6b146c68", "type": "sqlite", "z": "1170da0f4e4c8045", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "update network state", "x": 720, "y": 60, "wires": [ [ "bd5ec405f98497be" ] ] }, { "id": "7d466904dcc22a23", "type": "link in", "z": "4bc53f77f2c857ba", "name": "load all device(IN)", "links": [ "0468df9a5a271569", "b82931419a746738", "bd5ec405f98497be", "d2c2f0caf6bf039d", "8cf0a0a0434d1a75", "28aca6c3e73f9b87" ], "x": 95, "y": 260, "wires": [ [ "7a3b9b19df4c91d3" ] ] }, { "id": "efc6af2b1324e10d", "type": "ui_template", "z": "4bc53f77f2c857ba", "group": "fc6b92455ad3b35e", "name": "main sockets panel", "order": 1, "width": "14", "height": 10, "format": "<script>\nvar htmlTimer=[];\n(function(scope) {\n scope.jsonobj=\"\";\n scope.recordset={};\n \n \n scope.$watch('msg', function(msg) {\n \n if (msg) {\n if (msg.topic==\"RECORDSET\") \n {\n \n scope.recordset=msg.recordset;\n }\n if (msg.topic==\"TIMER\") {\n htmlTimer=msg.payload;\n //alert(htmlTimer);\n \n }\n \n }\n \n });\n /*\n scope.getRecordset = function() {\n \n return scope.recordset;\n }\n */\n scope.getTimer = function(deviceID) {\n var ret_val=\" \";\n \n if (htmlTimer.length == undefined) return ret_val;\n if (htmlTimer.length > 0) {\n \n for (i=0; i < htmlTimer.length; i++) {\n if (htmlTimer[i].deviceID==deviceID) {\n ret_val = htmlTimer[i].cmd+\": \"+htmlTimer[i].timeout+\" seconds\";\n break;\n } \n } \n \n \n }\n //alert(ret_val);\n return ret_val;\n \n \n }\n scope.sendCMD = function(sscommand) {\n scope.jsonobj=\"{\\\"Type\\\":\\\"CMD\\\",\\\"deviceID\\\":\\\"\"+sscommand.deviceID+\"\\\",\\\"SSCMD\\\":\"+((!sscommand.lastState)?\"\\\"ON\\\"\":\"\\\"OFF\\\"\")+\"}\";\n //alert(jsonobj);\n return scope.jsonobj;\n }\n scope.gotoSetting = function(device) {\n return \"{\\\"Type\\\":\\\"SETTINGS\\\",\\\"deviceID\\\":\\\"\"+device.deviceID+\"\\\",\\\"deviceName\\\":\\\"\"+device.deviceName+\"\\\"}\";\n }\n \n})(scope);\n \n \n \n</script>\n\n<div flex layout=\"row\" layout-wrap layout-align=\"space-between\" style=\"width:100%;height:100%;\">\n<div ng-repeat=\"mp in recordset\">\n <div flex layout=\"column\" style='background:{{(mp.networkState)?\"#FFFFFF\":\"#F0F0F0\"}}; \n width:160px; margin:2px; \n box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 6px 10px 0 rgba(0, 0, 0, .14), 0 1px 18px 0 rgba(0, 0, 0, .12);\n z-index: 20;'>\n <div>\n <spam style=\"float:left;margin-left:4px;\"><md-icon style='color:{{(mp.networkState)?\"blue\":\"#C0C0C0\"}}'>\n {{(mp.networkState)?\"wifi\":\"signal_wifi_0_bar\"}}\n </md-icon></spam>\n \n <spam style=\"float:right;margin-right:4px;\">\n <md-button ng-click=\"send({payload:gotoSetting(mp)})\" style='background:{{(mp.networkState)?\"#FFFFFF\":\"#F0F0F0\"}};'>\n <md-icon style=\"align:right\">settings</md-icon>\n </md-button>\n </spam>\n <spam style=\"float:right;margin-left:4px;\"><md-icon style='color:blue'>\n {{(mp.home)?\"home\":\"\"}}\n </md-icon></spam>\n </div>\n \n <div style=\"margin: auto;color:#008000;font-weight:bold\">{{mp.deviceName}}</div>\n \n <div style=\"margin: auto;padding:0px\"><img src='{{(mp.lastState)?\"/socket_connect.svg\":\"/socket_disconnect.svg\"}}' width='100px'></div>\n <div style=\"margin:auto;height:24px;\">{{getTimer(mp.deviceID)}}</div>\n \n <div style=\"margin:auto;margin-bottom:4px;\">\n <md-button ng-click=\"send({payload:sendCMD(mp)})\" style=\"width:80px;border-radius: 12px 12px 12px 12px;\">\n <md-icon style='color:{{(mp.lastState)?\"white\":\"blue\"}}'>settings_power</md-icon>\n <font style='color:{{(mp.lastState)?\"white\":\"blue\"}}'>{{(mp.lastState)?\"OFF\":\"ON\"}}</font>\n </md-button>\n \n </div>\n \n </div>\n</div>\n</div>\n\n", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 490, "y": 160, "wires": [ [ "d11d203a4c949cb0" ] ] }, { "id": "bd5ec405f98497be", "type": "link out", "z": "1170da0f4e4c8045", "name": "update pannel", "links": [ "7d466904dcc22a23" ], "x": 895, "y": 60, "wires": [] }, { "id": "d2c2f0caf6bf039d", "type": "link out", "z": "30074137ff8b6e70", "name": "update pannel", "links": [ "7d466904dcc22a23" ], "x": 655, "y": 140, "wires": [] }, { "id": "7a3b9b19df4c91d3", "type": "sqlite", "z": "4bc53f77f2c857ba", "mydb": "0c752464ee7109c3", "sqlquery": "fixed", "sql": "select deviceID, deviceName, lastState, networkState,\n (select count(*) from notAtHome B where A.deviceID = B.deviceID) as home from device A order by deviceName", "name": "select all device", "x": 220, "y": 260, "wires": [ [ "35afb493804ae053" ] ] }, { "id": "d11d203a4c949cb0", "type": "json", "z": "4bc53f77f2c857ba", "name": "", "property": "payload", "action": "", "pretty": false, "x": 450, "y": 240, "wires": [ [ "5371499fb4b4f440", "e473c609ee8b3d2f" ] ] }, { "id": "9f57d0424883ef48", "type": "mqtt out", "z": "4bc53f77f2c857ba", "name": "", "topic": "", "qos": "1", "retain": "", "respTopic": "", "contentType": "", "userProps": "", "correl": "", "expiry": "", "broker": "de64abcc7738c08d", "x": 630, "y": 60, "wires": [] }, { "id": "5371499fb4b4f440", "type": "function", "z": "4bc53f77f2c857ba", "name": "send to device via mqtt", "func": "var mqtt={};\nif (msg.payload[\"Type\"]==\"CMD\") {\n mqtt.topic = \"SmartSocket/\"+msg.payload[\"deviceID\"]+\"/Socket/CMD\";\n mqtt.payload=msg.payload[\"SSCMD\"];\n return mqtt;\n}\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 620, "y": 220, "wires": [ [ "9f57d0424883ef48" ] ] }, { "id": "f923bbd30bfac7b2", "type": "mqtt in", "z": "1170da0f4e4c8045", "name": "", "topic": "SmartSocket/+/Socket/State", "qos": "1", "datatype": "auto", "broker": "de64abcc7738c08d", "nl": false, "rap": true, "rh": 0, "x": 140, "y": 200, "wires": [ [ "d17fae4a552647cd" ] ] }, { "id": "d17fae4a552647cd", "type": "json", "z": "1170da0f4e4c8045", "name": "", "property": "payload", "action": "", "pretty": false, "x": 310, "y": 240, "wires": [ [ "ec9dc0e95e90d720" ] ] }, { "id": "f70d640e01e7202c", "type": "sqlite", "z": "1170da0f4e4c8045", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "update socket state", "x": 710, "y": 240, "wires": [ [ "0468df9a5a271569" ] ] }, { "id": "ec9dc0e95e90d720", "type": "function", "z": "1170da0f4e4c8045", "name": "update socket state", "func": "var ls=0;\nif (msg.payload[\"State\"] == \"ON\") ls =1;\nmsg.topic=\"Update device set lastState=\"+ls +\" where deviceID='\"+msg.payload[\"ID\"]+\"'\";\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 490, "y": 240, "wires": [ [ "f70d640e01e7202c" ] ] }, { "id": "0468df9a5a271569", "type": "link out", "z": "1170da0f4e4c8045", "name": "", "links": [ "7d466904dcc22a23" ], "x": 895, "y": 240, "wires": [] }, { "id": "35afb493804ae053", "type": "function", "z": "4bc53f77f2c857ba", "name": "prepare msg payload", "func": "var rs={};\nrs.recordset=[];\nrs.recordset=msg.payload;\nrs.topic=\"RECORDSET\";\nreturn rs;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 240, "y": 180, "wires": [ [ "efc6af2b1324e10d" ] ] }, { "id": "acf62abd22b90d1b", "type": "inject", "z": "4bc53f77f2c857ba", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "1", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "TIMER", "payloadType": "date", "x": 130, "y": 60, "wires": [ [ "13f2fe26d8c8d9b3" ] ] }, { "id": "21efaace9b1a2876", "type": "ui_ui_control", "z": "4bc53f77f2c857ba", "name": "", "events": "all", "x": 780, "y": 280, "wires": [ [ "46840fc0f3f2dd33" ] ] }, { "id": "e473c609ee8b3d2f", "type": "function", "z": "4bc53f77f2c857ba", "name": "go to Settings tab", "func": "var settingUI={};\n\nif (msg.payload[\"Type\"]==\"SETTINGS\") {\n settingUI.deviceID=msg.payload[\"deviceID\"];\n settingUI.payload = {};\n settingUI.payload.tab = \"Settings\";\n global.set(\"deviceID\", msg.payload[\"deviceID\"]);\n global.set(\"deviceName\", msg.payload[\"deviceName\"]);\n return settingUI;\n}\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 280, "wires": [ [ "21efaace9b1a2876" ] ] }, { "id": "0fc3c8e3128af58d", "type": "ui_template", "z": "439e43cb848a5281", "group": "8a15ca423c637161", "name": "Settings", "order": 3, "width": 0, "height": 0, "format": "\n<script>\nscope.on_off=\"OFF\";\n scope.hour=0;\n scope.minute=0;\n scope.bhour=18;\n scope.bminute=0;\n scope.ehour=23;\n scope.eminute=0;\n scope.deviceName=\"\";\n scope.deviceID=\"\";\n scope.buttonSetTimer=\"Set Timer\";\n scope.noAtHomeSet=\"Set\"\n scope.notAtHomeCancel=\"Cance\";\n scope.schedule={};\n scope.sch_w1=0;\n scope.sch_w2=0;\n scope.sch_w3=0;\n scope.sch_w4=0;\n scope.sch_w5=0;\n scope.sch_w6=0;\n scope.sch_w0=0;\n scope.sch_hour=0;\n scope.sch_min=0;\n scope.sch_on_off=\"ON\";\n scope.sch_rs=[];\n(function(scope) {\n \n scope.$watch('msg', function(msg) {\n if (msg) {\n if (msg.topic==\"setDeviceName\"){\n scope.deviceName=msg.payload.deviceName;\n scope.deviceID=msg.payload.deviceID;\n \n }\n if (msg.topic==\"SCHEDULERS\"){\n scope.sch_rs=msg.payload;\n \n }\n \n }\n \n });\n scope.handleChangeHour = function() {\n if (isNaN(scope.hour)) scope.hour=0;\n \n }\n scope.handleChangeMinute = function() {\n if (isNaN(scope.minute)) scope.minute=0;\n }\n scope.setTimer = function() {\n var tt = (scope.hour*60+scope.minute)*1;\n scope.buttonSetTimer=\"Timer Started\";\n return \"{\\\"topic\\\":\\\"TIMER\\\",\\\"payload\\\":{\\\"TIMEOUT\\\":\"+tt+\",\\\"CMD\\\":\\\"\"+scope.on_off+\"\\\"}}\";\n }\n \n scope.buttonNotAtHomeSet = function() {\n var bt = scope.bhour*60+scope.bminute;\n var et = scope.ehour*60+scope.eminute;\n var rbt = bt+Math.floor(Math.random()*60-30);\n var ret = et+Math.floor(Math.random()*60-30);\n return \"{\\\"topic\\\":\\\"NOTATHOMESET\\\",\\\"payload\\\":{\\\"BTIME\\\":\"+bt+\",\\\"ETIME\\\":\"+et+\",\\\"RBTIME\\\":\"+rbt+\",\\\"RETIME\\\":\"+ret+\",\\\"deviceID\\\":\\\"\"+scope.deviceID+\"\\\"}}\";\n \n }\n scope.buttonNotAtHomeCancel = function() {\n return \"{\\\"topic\\\":\\\"NOTATHOMECANCEL\\\",\\\"payload\\\":{\\\"deviceID\\\":\\\"\"+scope.deviceID+\"\\\"}}\";\n \n }\n \n \n scope.addSchedule = function() {\n return \"{\\\"topic\\\":\\\"SCHEDULEADD\\\",\\\"payload\\\":{\\\"deviceID\\\":\\\"\"+scope.deviceID+\"\\\"\"+\n \",\\\"w1\\\":\"+scope.sch_w1+\",\\\"w2\\\":\"+scope.sch_w2+\",\\\"w3\\\":\"+scope.sch_w3+\",\\\"w4\\\":\"+scope.sch_w4+\n \",\\\"w5\\\":\"+scope.sch_w5+\",\\\"w6\\\":\"+scope.sch_w6+\",\\\"w0\\\":\"+scope.sch_w0+\n \",\\\"stime\\\":\\\"\"+scope.sch_hour+\":\"+scope.sch_min+\"\\\",\\\"cmd\\\":\\\"\"+scope.sch_on_off+\"\\\"}}\";\n }\n scope.deleteOnScheduleRecord = function(id) {\n return \"{\\\"topic\\\":\\\"SCHEDULECANCEL\\\",\\\"payload\\\":{\\\"rowid\\\":\"+id+\"}}\";\n }\n})(scope);\n \n </script> \n<div style=\"color:blue;font-size:large;font-weight:bold; margin:auto\">{{deviceName}}</div>\n<div style=\"display:flex;margin:auto;\">\n <!-- Set Timer start here--> \n <div style=\"width:350px; height:170px;padding:5px;box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 6px 10px 0 rgba(0, 0, 0, .14), 0 1px 18px 0 rgba(0, 0, 0, .12);\n z-index: 20;text-align:center;\">\n <div style=\"margin:auto;color:blue;font-size:large;font-weight:bold\">Set Timer</div>\n<table width=\"100%\">\n<tr>\n<td align=\"center\">\n<spam><i style=\"color:blue;padding:2px;\" class=\"fa fa-clock-o fa-2x\" aria-hidden=\"true\"></i></spam>\n<spam><input type=\"number\" ng-model=\"hour\" style=\"width:40px\" min=\"0\" max=\"59\" value=0 ng-change=\"handleChangeHour()\">minutes</spam>\n<spam><input type=\"number\" ng-model=\"minute\" style=\"width:40px\" min=\"0\" max=\"59\" value=0 ng-change=\"handleChangeMinute()\">seconds</spam>\n</td>\n</tr>\n<tr>\n<td align=\"center\">\n\t<input type=\"radio\" ng-model=\"on_off\" value=\"ON\">ON \n <input type=\"radio\" ng-model=\"on_off\" value=\"OFF\">OFF\n</td>\n</tr>\n<tr>\n<td align=\"center\" height=\"120%\">\n<md-button class=\"rounded\" ng-click=\"send({payload:setTimer()})\"><md-icon style=\"color:white;\">timer</md-icon>{{buttonSetTimer}}</md-button>\n</td>\n</tr>\n</table>\n \n</div>\n<!-- set timer end -->\n\n<!-- not at home start here-->\n<div style=\"width:400px; height:170px;padding:5px;box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 6px 10px 0 rgba(0, 0, 0, .14), 0 1px 18px 0 rgba(0, 0, 0, .12);\n z-index: 20;\">\n \n<table width=\"100%\">\n <tr>\n <td style=\"color:blue;font-size:large;font-weight:bold;text-align:center\">\n Not At Home\n </td>\n </tr>\n <tr>\n <td align=\"right\">\n <md-icon style=\"color:blue\">power</md-icon>\"Power ON\" between\n <input type=\"number\" ng-model=\"bhour\" style=\"width:35px\" min=\"0\" max=\"23\" value=0 ng-change=\"handleChangeHour()\">hours\n <input type=\"number\" ng-model=\"bminute\" style=\"width:35px\" min=\"0\" max=\"59\" value=0 ng-change=\"handleChangeMinute()\">minutes\n </td>\n </tr>\n\t<tr>\n\t<td style=\"text-align:right;margin-right:1em;\"> \n\tand\n\t</td>\n\t</tr>\n\t<tr>\n\t<td style=\"text-align:right;margin-right:1em;\">\n\t<spam><input type=\"number\" ng-model=\"ehour\" style=\"width:35px\" min=\"0\" max=\"23\" value=0 ng-change=\"handleChangeHour()\">hours</spam>\n <spam><input type=\"number\" ng-model=\"eminute\" style=\"width:35px\" min=\"0\" max=\"59\" value=0 ng-change=\"handleChangeMinute()\">minutes</spam>\n </td>\n\t</tr>\n\t<tr>\n\t<td style=\"text-align:center;margin-right:1em;\">\n\t<spam><md-button class=\"rounded\" ng-click=\"send({payload:buttonNotAtHomeSet()})\"><md-icon style=\"color:white;\">query_builder</md-icon>{{noAtHomeSet}}</md-button></spam> \n\t<spam><md-button class=\"rounded\" ng-click=\"send({payload:buttonNotAtHomeCancel()})\"><md-icon style=\"color:red;\">cancel</md-icon>{{notAtHomeCancel}}</md-button></spam>\n\t</td>\n\t</tr>\n</table>\n\n</div>\n<!-- not at home end -->\n</div>\n\n\n<!-- schedule -->\n <div style=\"margin:auto;width:90%; padding:5px;box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 6px 10px 0 rgba(0, 0, 0, .14), 0 1px 18px 0 rgba(0, 0, 0, .12);\n z-index: 20;text-align:center;\">\n <div style=\"margin:auto;color:blue;font-size:large;font-weight:bold\">Schedule</div>\n <div>\n <spam><input type=\"checkbox\" ng-model=\"sch_w1\">MON</spam> \n <spam><input type=\"checkbox\" ng-model=\"sch_w2\">TUE</spam>\n <spam><input type=\"checkbox\" ng-model=\"sch_w3\">WED</spam>\n <spam><input type=\"checkbox\" ng-model=\"sch_w4\">THU</spam>\n <spam><input type=\"checkbox\" ng-model=\"sch_w5\">FRI</spam>\n <spam><input type=\"checkbox\" ng-model=\"sch_w6\">SAT</spam>\n <spam><input type=\"checkbox\" ng-model=\"sch_w0\">SUN</spam>\n <spam><input type=\"number\" width=\"20px\" min=\"0\" max=\"23\" ng-model=\"sch_hour\">Hour</spam>\n <spam><input type=\"number\" width=\"20px\" min=\"0\" max=\"59\" ng-model=\"sch_min\">Min</spam>\n <spam><input type=\"radio\" ng-model=\"sch_on_off\" value=\"ON\">ON \n <input type=\"radio\" ng-model=\"sch_on_off\" value=\"OFF\">OFF</spam>\n <spam><md-button class=\"rounded\" ng-click=\"send({payload:addSchedule()})\"><md-icon style=\"color:white;\">alarm_add</md-icon></md-button></spam>\n </div> \n\n<table width=\"80%\" style=\"margin-left:auto;margin-right:auto; border:solid 1px;border-collapse:collapse\">\n <tr style=\"border-bottom:solid 1px;\">\n\n <th colspan=\"7\" width=\"70%\">Weekday</td>\n <th>Time</td>\n <th>Power</td>\n <th> </td>\n\n </tr>\n\n <tr style=\"border-bottom:dash 1px;\" ng-repeat=\"record in sch_rs\">\n <td width=\"10%\">{{(record.w1)?\"MON\":\"\"}}</td>\n <td width=\"10%\">{{(record.w2)?\"TUE\":\"\"}}</td>\n <td width=\"10%\">{{(record.w3)?\"WED\":\"\"}}</td>\n <td width=\"10%\">{{(record.w4)?\"THU\":\"\"}}</td>\n <td width=\"10%\">{{(record.w5)?\"FRI\":\"\"}}</td>\n <td width=\"10%\">{{(record.w6)?\"SAT\":\"\"}}</td>\n <td width=\"10%\">{{(record.w0)?\"SUN\":\"\"}}</td>\n <td align=\"center\">{{record.stime}}</td>\n <td align=\"center\">{{record.cmd}}</td>\n <td align=\"center\"><md-button class=\"rounded\" ng-click=\"send({payload:deleteOnScheduleRecord(record.rowid)})\"><md-icon style=\"color:white;\">delete</md-icon></md-button></td>\n \n </tr> \n \n\n</table>\n</div>\n<!-- schedule -->", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 180, "y": 60, "wires": [ [ "15f0595c9855419e" ] ] }, { "id": "f6a59d40a352b0d7", "type": "ui_button", "z": "439e43cb848a5281", "name": "", "group": "8a15ca423c637161", "order": 5, "width": 0, "height": 0, "passthru": false, "label": "Back", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "arrow_back", "payload": "{\"tab\":\"Home\"}", "payloadType": "json", "topic": "topic", "topicType": "msg", "x": 430, "y": 120, "wires": [ [ "cbef3ed26dc04669" ] ] }, { "id": "c809ff94aedd53ff", "type": "function", "z": "439e43cb848a5281", "name": "set Timer", "func": "var m = {};\nvar device_timeout = [];\nm.deviceID=global.get(\"deviceID\");\nm.timeout=msg.payload.TIMEOUT;\nm.cmd=msg.payload.CMD;\nmsg.payload=m;\n\nif (global.get(\"device_timeout\")==undefined) global.set(\"device_timeout\",[]);\ndevice_timeout = global.get(\"device_timeout\");\nfor (i =0; i < device_timeout.length; i++) {\n if (device_timeout[i].deviceID==m.deviceID) {\n device_timeout.splice(i,1);\n break;\n }\n}\n\ndevice_timeout.push(m);\n\nglobal.set(\"device_timeout\",device_timeout);\n\n\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 340, "y": 160, "wires": [ [] ] }, { "id": "a3a8ae9391861515", "type": "link in", "z": "439e43cb848a5281", "name": "Settings starting", "links": [ "33da9a0c5c471908", "5184a259947998c0" ], "x": 55, "y": 60, "wires": [ [ "0fc3c8e3128af58d" ] ] }, { "id": "13f2fe26d8c8d9b3", "type": "function", "z": "4bc53f77f2c857ba", "name": "any Timer is set", "func": "if (global.get(\"device_timeout\")==undefined ) {\n return [];\n} else {\n if (global.get(\"device_timeout\").length==0) {\n return [];\n }\n}\nvar devices_msg={};\ndevices_msg.payload=[];\n\nfor (i=0; i < global.get(\"device_timeout\").length; i++) {\n if ((global.get(\"device_timeout\")[i].timeout--)==0) {\n devices_msg.payload.push({\"deviceID\":global.get(\"device_timeout\")[i].deviceID,\"cmd\":global.get(\"device_timeout\")[i].cmd});\n global.get(\"device_timeout\").splice(i,1);\n }\n}\nmsg.topic=\"TIMER\";\nmsg.payload = global.get(\"device_timeout\");\n\nreturn [devices_msg,msg];", "outputs": 2, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 120, "wires": [ [ "3e63f34496452891" ], [ "efc6af2b1324e10d" ] ] }, { "id": "3e63f34496452891", "type": "function", "z": "4bc53f77f2c857ba", "name": "timeout to MQTT", "func": "if (msg.payload==null) return;\nvar devices=[];\ndevices=msg.payload;\n\nif(devices.length > 0) {\n for (i=0; i< devices.length; i++) {\n msg.topic=\"SmartSocket/\"+devices[i].deviceID+\"/Socket/CMD\";\n msg.payload=devices[i].cmd;\n node.send(msg);\n }\n}\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 430, "y": 60, "wires": [ [ "9f57d0424883ef48" ] ] }, { "id": "7d4ba8302ecf747a", "type": "function", "z": "439e43cb848a5281", "name": "retrive payload", "func": "var m={};\nm.payload = msg.payload.payload;\nm.topic = msg.payload.topic;\nreturn m;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 140, "y": 120, "wires": [ [ "00a07b17407a70b9" ] ] }, { "id": "15f0595c9855419e", "type": "json", "z": "439e43cb848a5281", "name": "", "property": "payload", "action": "", "pretty": false, "x": 330, "y": 60, "wires": [ [ "7d4ba8302ecf747a" ] ] }, { "id": "00a07b17407a70b9", "type": "switch", "z": "439e43cb848a5281", "name": "", "property": "topic", "propertyType": "msg", "rules": [ { "t": "eq", "v": "TIMER", "vt": "str" }, { "t": "eq", "v": "NOTATHOMESET", "vt": "str" }, { "t": "eq", "v": "NOTATHOMECANCEL", "vt": "str" }, { "t": "eq", "v": "SCHEDULEADD", "vt": "str" }, { "t": "eq", "v": "SCHEDULECANCEL", "vt": "str" } ], "checkall": "true", "repair": false, "outputs": 5, "x": 90, "y": 220, "wires": [ [ "c809ff94aedd53ff" ], [ "08f21e319a0704b6" ], [ "34e3bb851f803d73" ], [ "119e1ef365315558" ], [ "5e78b9e1042d35f6" ] ] }, { "id": "85a329500822475d", "type": "function", "z": "4bc53f77f2c857ba", "name": "load device data", "func": "if (msg.payload==\"change\")\n return msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 620, "y": 360, "wires": [ [ "28aca6c3e73f9b87" ] ] }, { "id": "28aca6c3e73f9b87", "type": "link out", "z": "4bc53f77f2c857ba", "name": "load all devices(OUT)", "links": [ "7d466904dcc22a23" ], "x": 795, "y": 360, "wires": [] }, { "id": "46840fc0f3f2dd33", "type": "switch", "z": "4bc53f77f2c857ba", "name": "which Tab", "property": "name", "propertyType": "msg", "rules": [ { "t": "eq", "v": "Home", "vt": "str" }, { "t": "eq", "v": "Settings", "vt": "str" } ], "checkall": "true", "repair": false, "outputs": 2, "x": 400, "y": 360, "wires": [ [ "85a329500822475d" ], [ "1a255c93d38aa3b4" ] ] }, { "id": "1a255c93d38aa3b4", "type": "function", "z": "4bc53f77f2c857ba", "name": "load global deviceName", "func": "msg={};\nmsg.topic=\"setDeviceName\";\nmsg.payload={};\nmsg.payload.deviceName = global.get(\"deviceName\");\nmsg.payload.deviceID = global.get(\"deviceID\");\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 420, "wires": [ [ "33da9a0c5c471908", "c83ce79a4a41f31b" ] ] }, { "id": "33da9a0c5c471908", "type": "link out", "z": "4bc53f77f2c857ba", "name": "goto Settings", "links": [ "a3a8ae9391861515" ], "x": 815, "y": 420, "wires": [] }, { "id": "cbef3ed26dc04669", "type": "ui_ui_control", "z": "439e43cb848a5281", "name": "", "events": "all", "x": 580, "y": 120, "wires": [ [] ] }, { "id": "08f21e319a0704b6", "type": "function", "z": "439e43cb848a5281", "name": "Not At Home DB", "func": "var qm = {};\n\nqm.topic = \"SELECT count(*) as rs,\"+\n msg.payload.BTIME+\" as BTIME,\"+msg.payload.ETIME+\" as ETIME,\"+\n msg.payload.RBTIME+\" as RBTIME,\"+msg.payload.RETIME+\" as RETIME,'\"+\n msg.payload.deviceID+\"' as deviceID from notAtHome where deviceID='\"+msg.payload.deviceID+\"'\";\n\n\nreturn qm;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 320, "y": 200, "wires": [ [ "8b1d782112d40d67" ] ] }, { "id": "8b1d782112d40d67", "type": "sqlite", "z": "439e43cb848a5281", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "record exist", "x": 310, "y": 260, "wires": [ [ "8c0704ce9f016fc5" ] ] }, { "id": "8c0704ce9f016fc5", "type": "function", "z": "439e43cb848a5281", "name": "insert or update", "func": "if (msg.payload[0].rs > 0) {\n msg.topic = \"update notAtHome set startTime=\"+msg.payload[0].BTIME+\n \",endTime=\"+msg.payload[0].ETIME+\",randomStartTime=\"+msg.payload[0].RBTIME+\n \",randomEndTime=\"+msg.payload[0].RETIME+\" where deviceID='\"+\n msg.payload[0].deviceID+\"'\";\n} else {\n msg.topic=\"Insert into notAtHome(deviceID, startTime,randomStartTime, endTime, randomEndTime) values('\"+\n msg.payload[0].deviceID+\"',\"+msg.payload[0].BTIME+\",\"+msg.payload[0].RBTIME+\",\"+\n msg.payload[0].ETIME+\",\"+msg.payload[0].RETIME+\")\";\n}\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 320, "y": 320, "wires": [ [ "35d803a5ac38b25f" ] ] }, { "id": "35d803a5ac38b25f", "type": "sqlite", "z": "439e43cb848a5281", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "update notAtHome", "x": 530, "y": 280, "wires": [ [] ] }, { "id": "34e3bb851f803d73", "type": "function", "z": "439e43cb848a5281", "name": "cancel notAtHomeDB", "func": "msg.topic = \"delete from notAtHome where deviceID='\"+\n msg.payload.deviceID+\"'\";\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 320, "y": 360, "wires": [ [ "d62377f9cf6a2f10" ] ] }, { "id": "d62377f9cf6a2f10", "type": "sqlite", "z": "439e43cb848a5281", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "delete notAtHome Record", "x": 550, "y": 360, "wires": [ [] ] }, { "id": "96234fb3dffef340", "type": "inject", "z": "1170da0f4e4c8045", "name": "check schedule timer", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "40", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payloadType": "date", "x": 120, "y": 340, "wires": [ [ "12b72a7ccf8b71c9", "51a171751b468ca0", "6ee23180b9c21fa6" ] ] }, { "id": "12b72a7ccf8b71c9", "type": "sqlite", "z": "1170da0f4e4c8045", "mydb": "0c752464ee7109c3", "sqlquery": "fixed", "sql": "select A.deviceID, A.startTime from notAtHome A, device B where A.deviceID=B.deviceID and b.lastState = 0 and strftime(\"%H\",Time ( 'now', 'localtime' ))*60+strftime(\"%MH\",Time ( 'now', 'localtime' )) = randomStartTime\n", "name": "check ON notAtHome DB", "x": 370, "y": 320, "wires": [ [ "af36dd03367a2452" ] ] }, { "id": "51a171751b468ca0", "type": "sqlite", "z": "1170da0f4e4c8045", "mydb": "0c752464ee7109c3", "sqlquery": "fixed", "sql": "select A.deviceID, A.endTime from notAtHome A, device B where A.deviceID=B.deviceID and b.lastState = 1 and strftime(\"%H\",Time ( 'now', 'localtime' ))*60+strftime(\"%MH\",Time ( 'now', 'localtime' )) = randomEndTime\n", "name": "check OFF notAtHome", "x": 380, "y": 380, "wires": [ [ "f4ed1537f199e2c4" ] ] }, { "id": "af36dd03367a2452", "type": "function", "z": "1170da0f4e4c8045", "name": "Power ON", "func": "var mqtt={};\nvar db={};\nvar randTime=0;\nif (msg.payload.length == 0) return;\nfor (i=0; i < msg.payload.length; i++) {\n randTime = msg.payload[i].startTime+Math.floor((Math.random()*60-30));\n db.topic = \"update notAtHome set randomStartTime=\"+randTime+\n \" where deviceID='\"+msg.payload[i].deviceID+\"'\";\n mqtt.topic = \"SmartSocket/\"+msg.payload[i].deviceID+\"/Socket/CMD\";\n mqtt.payload=\"ON\";\n node.send([db,mqtt]);\n}\n", "outputs": 2, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 570, "y": 320, "wires": [ [ "f0192ebdb030dd97" ], [ "fc6287b6085264c4" ] ] }, { "id": "f4ed1537f199e2c4", "type": "function", "z": "1170da0f4e4c8045", "name": "Power Off", "func": "var mqtt={};\nvar db={};\nvar randTime=0;\nif (msg.payload.length == 0) return;\nfor (i=0; i < msg.payload.length; i++) {\n randTime = msg.payload[i].endTime+Math.floor((Math.random()*60-30));\n db.topic = \"update notAtHome set randomEndTime=\"+randTime+\n \" where deviceID='\"+msg.payload[i].deviceID+\"'\";\n mqtt.topic = \"SmartSocket/\"+msg.payload[i].deviceID+\"/Socket/CMD\";\n mqtt.payload=\"OFF\";\n node.send([mqtt,db]);\n}\n", "outputs": 2, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 560, "y": 380, "wires": [ [ "fc6287b6085264c4" ], [ "32fb4c7134eda469" ] ] }, { "id": "fc6287b6085264c4", "type": "mqtt out", "z": "1170da0f4e4c8045", "name": "notAtHome-ON-OFF", "topic": "", "qos": "1", "retain": "", "respTopic": "", "contentType": "", "userProps": "", "correl": "", "expiry": "", "broker": "de64abcc7738c08d", "x": 800, "y": 360, "wires": [] }, { "id": "f0192ebdb030dd97", "type": "sqlite", "z": "1170da0f4e4c8045", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "next Random ON Time", "x": 780, "y": 300, "wires": [ [] ] }, { "id": "32fb4c7134eda469", "type": "sqlite", "z": "1170da0f4e4c8045", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "next Random OFF Time", "x": 810, "y": 420, "wires": [ [] ] }, { "id": "17d673196b252993", "type": "ui_template", "z": "439e43cb848a5281", "group": "8a15ca423c637161", "name": "css", "order": 1, "width": 0, "height": 0, "format": "<style>\n .rounded {\n border-radius: 12px 12px 12px 12px;\n}\n.tdcss {\n text-align:right;\n}\n\n \n</style>\n", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 500, "y": 40, "wires": [ [] ] }, { "id": "119e1ef365315558", "type": "function", "z": "439e43cb848a5281", "name": "add Schedule", "func": "msg.topic=\"insert into schedule (deviceID,w1,w2,w3,w4,w5,w6,w0,stime,cmd) values ('\"+\n msg.payload.deviceID+\"',\"+msg.payload.w1+\",\"+msg.payload.w2+\n \",\"+msg.payload.w3+\",\"+msg.payload.w4+\",\"+msg.payload.w5+\n \",\"+msg.payload.w6+\",\"+msg.payload.w0+\",'\"+msg.payload.stime+\"','\"+msg.payload.cmd+\"')\";\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 300, "y": 420, "wires": [ [ "9b8641728b6c419e" ] ] }, { "id": "9b8641728b6c419e", "type": "sqlite", "z": "439e43cb848a5281", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "add Schedule Record", "x": 540, "y": 420, "wires": [ [ "24467af1f6ef4626" ] ] }, { "id": "e58a44cb5a086ccb", "type": "sqlite", "z": "439e43cb848a5281", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "fetch all schedule", "x": 270, "y": 580, "wires": [ [ "ca5aa488d1183ff4" ] ] }, { "id": "24467af1f6ef4626", "type": "link out", "z": "439e43cb848a5281", "name": "fetch schedule(OUT)", "links": [ "4d16f19dda5aa1c3" ], "x": 695, "y": 440, "wires": [] }, { "id": "e6bef7ab409ec58e", "type": "link in", "z": "439e43cb848a5281", "name": "", "links": [], "x": -35, "y": 500, "wires": [ [] ] }, { "id": "4d16f19dda5aa1c3", "type": "link in", "z": "439e43cb848a5281", "name": "fetch schedule(IN)", "links": [ "24467af1f6ef4626", "c83ce79a4a41f31b", "dc3176d3a803559f" ], "x": 75, "y": 580, "wires": [ [ "efdd09522857585c" ] ] }, { "id": "c02dbec01ee3a152", "type": "comment", "z": "439e43cb848a5281", "name": "fetch schedule", "info": "", "x": 110, "y": 540, "wires": [] }, { "id": "194f3bf15b2f34be", "type": "comment", "z": "439e43cb848a5281", "name": "fetch schedule", "info": "", "x": 720, "y": 400, "wires": [] }, { "id": "efdd09522857585c", "type": "function", "z": "439e43cb848a5281", "name": "schedule records", "func": "msg.topic = \"select rowid, deviceID, w1,w2,w3,w4,w5,w6,w0,stime,cmd from schedule where deviceID='\"+global.get(\"deviceID\")+\"'\";\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 640, "wires": [ [ "e58a44cb5a086ccb" ] ] }, { "id": "5184a259947998c0", "type": "link out", "z": "439e43cb848a5281", "name": "", "links": [ "a3a8ae9391861515" ], "x": 635, "y": 580, "wires": [] }, { "id": "9ed4c419cf2c134b", "type": "comment", "z": "439e43cb848a5281", "name": "Settings Start", "info": "", "x": 630, "y": 620, "wires": [] }, { "id": "ca5aa488d1183ff4", "type": "function", "z": "439e43cb848a5281", "name": "send msg to template", "func": "msg.topic=\"SCHEDULERS\";\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 580, "wires": [ [ "5184a259947998c0" ] ] }, { "id": "c83ce79a4a41f31b", "type": "link out", "z": "4bc53f77f2c857ba", "name": "fetch schedule(OUT)", "links": [ "4d16f19dda5aa1c3" ], "x": 815, "y": 460, "wires": [] }, { "id": "5e78b9e1042d35f6", "type": "function", "z": "439e43cb848a5281", "name": "delete Schedule", "func": "msg.topic=\"delete from schedule where rowid=\"+msg.payload.rowid;\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 300, "y": 480, "wires": [ [ "9b8641728b6c419e" ] ] }, { "id": "6ee23180b9c21fa6", "type": "function", "z": "1170da0f4e4c8045", "name": "perform schedule", "func": "var date = new Date(msg.payload);\nvar w = date.getDay();\nvar h=date.getHours();\nvar m=date.getMinutes();\nmsg.topic=\"SELECT deviceID, cmd from schedule where w\"+w+\"=1 and stime='\"+h+\":\"+m+\"'\";\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 480, "wires": [ [ "3da5e56a4fd812b1" ] ] }, { "id": "3da5e56a4fd812b1", "type": "sqlite", "z": "1170da0f4e4c8045", "mydb": "0c752464ee7109c3", "sqlquery": "msg.topic", "sql": "", "name": "records in schedule", "x": 510, "y": 480, "wires": [ [ "2a10906e87688185" ] ] }, { "id": "2a10906e87688185", "type": "function", "z": "1170da0f4e4c8045", "name": "send to mqtt", "func": "var mqtt={};\nif (msg.payload.length > 0) {\n for (i=0; i < msg.payload.length; i++) {\n mqtt.topic = \"SmartSocket/\"+msg.payload[i].deviceID+\"/Socket/CMD\";\n mqtt.payload = msg.payload[i].cmd;\n node.send(mqtt);\n }\n}\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 690, "y": 480, "wires": [ [ "3f816e90ed9524a8" ] ] }, { "id": "3f816e90ed9524a8", "type": "mqtt out", "z": "1170da0f4e4c8045", "name": "", "topic": "", "qos": "1", "retain": "", "respTopic": "", "contentType": "", "userProps": "", "correl": "", "expiry": "", "broker": "de64abcc7738c08d", "x": 870, "y": 480, "wires": [] }, { "id": "16eca474e0a71522", "type": "comment", "z": "1170da0f4e4c8045", "name": "To Update Socket Panel", "info": "", "x": 860, "y": 100, "wires": [] }, { "id": "c7f182c9be7ea62f", "type": "comment", "z": "1170da0f4e4c8045", "name": "To Update Socket Panel", "info": "", "x": 840, "y": 200, "wires": [] }, { "id": "decc1ef90f12a1fb", "type": "comment", "z": "30074137ff8b6e70", "name": "from root flow", "info": "", "x": 90, "y": 60, "wires": [] }, { "id": "eb0629e281b3522e", "type": "comment", "z": "30074137ff8b6e70", "name": "to Update Socket Panel", "info": "", "x": 600, "y": 180, "wires": [] } ]
沒有留言:
張貼留言