Remote_AC1/Web Node-RED Air Conditione...

858 lines
33 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[
{
"id": "dd166f1fb1414356",
"type": "tab",
"label": "Dashboard AC",
"disabled": false,
"info": ""
},
{
"id": "45a2b398537a5711",
"type": "ui_switch",
"z": "dd166f1fb1414356",
"name": "Power",
"label": "Power",
"tooltip": "",
"group": "8bd4a1c79f424e45",
"order": 1,
"width": 3,
"height": 1,
"passthru": true,
"decouple": "false",
"topic": "ac/power",
"topicType": "str",
"style": "",
"onvalue": "on",
"onvalueType": "str",
"onicon": "",
"oncolor": "",
"offvalue": "off",
"offvalueType": "str",
"officon": "",
"offcolor": "",
"animate": true,
"className": "",
"x": 210,
"y": 60,
"wires": [
[
"e26985a773758b5b",
"fed55988a21fe62d"
]
]
},
{
"id": "e09b28a2caeb4107",
"type": "ui_dropdown",
"z": "dd166f1fb1414356",
"name": "Swing",
"label": "Swing",
"tooltip": "",
"place": "Select option",
"group": "8bd4a1c79f424e45",
"order": 4,
"width": 6,
"height": 1,
"passthru": true,
"multiple": false,
"options": [
{
"label": "Swing ON",
"value": "on",
"type": "str"
},
{
"label": "Swing OFF",
"value": "off",
"type": "str"
}
],
"topic": "ac/swing",
"topicType": "str",
"className": "",
"x": 210,
"y": 180,
"wires": [
[
"e26985a773758b5b",
"fed55988a21fe62d"
]
]
},
{
"id": "e26985a773758b5b",
"type": "mqtt out",
"z": "dd166f1fb1414356",
"name": "MQTT KONTROL AC",
"topic": "",
"qos": "",
"retain": "",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "9fbca1958dadbcd5",
"x": 480,
"y": 60,
"wires": []
},
{
"id": "f7fd6d577815fc4e",
"type": "mqtt in",
"z": "dd166f1fb1414356",
"name": "",
"topic": "sensor/dht22",
"qos": "2",
"datatype": "json",
"broker": "9fbca1958dadbcd5",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 1070,
"y": 380,
"wires": [
[
"4daa938f22269682",
"e834877d3f06063a",
"1c4be2044b325aeb"
]
]
},
{
"id": "e834877d3f06063a",
"type": "function",
"z": "dd166f1fb1414356",
"name": "function 2",
"func": "var suhu = { payload: msg.payload.suhu };\nvar kelembapan = { payload: msg.payload.kelembapan };\nreturn [\n { payload: msg.payload.suhu },\n { payload: msg.payload.kelembapan }\n];\n\nlet data = JSON.parse(msg.payload);\nmsg.payload = {\n suhu: data.temperature,\n kelembapan: data.humidity,\n waktu: new Date().toLocaleString()\n};\nreturn msg;",
"outputs": 2,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1280,
"y": 380,
"wires": [
[
"6bd53add46a3c498"
],
[
"00983c16aeed32b2"
]
]
},
{
"id": "6bd53add46a3c498",
"type": "ui_gauge",
"z": "dd166f1fb1414356",
"name": "",
"group": "565ebc18e3d1af6f",
"order": 2,
"width": 6,
"height": 4,
"gtype": "gage",
"title": "Suhu",
"label": "°C",
"format": "{{value}} °C",
"min": 0,
"max": "50",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 1450,
"y": 360,
"wires": []
},
{
"id": "00983c16aeed32b2",
"type": "ui_gauge",
"z": "dd166f1fb1414356",
"name": "",
"group": "565ebc18e3d1af6f",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Kelembapan",
"label": "%",
"format": "{{value}} %",
"min": 0,
"max": "100",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 1470,
"y": 400,
"wires": []
},
{
"id": "cf15d38ecdd8677a",
"type": "ui_dropdown",
"z": "dd166f1fb1414356",
"name": "Fan Speed",
"label": "Fan Speed",
"tooltip": "",
"place": "Select option",
"group": "8bd4a1c79f424e45",
"order": 5,
"width": 6,
"height": 1,
"passthru": true,
"multiple": false,
"options": [
{
"label": "Auto",
"value": "auto",
"type": "str"
},
{
"label": "Low",
"value": "low",
"type": "str"
},
{
"label": "Medium",
"value": "medium",
"type": "str"
},
{
"label": "High",
"value": "high",
"type": "str"
}
],
"topic": "ac/fan",
"topicType": "str",
"className": "",
"x": 190,
"y": 240,
"wires": [
[
"e26985a773758b5b",
"fed55988a21fe62d"
]
]
},
{
"id": "789737a05200ad73",
"type": "ui_button",
"z": "dd166f1fb1414356",
"name": "Reset WiFi ESP32",
"group": "565ebc18e3d1af6f",
"order": 6,
"width": 6,
"height": 1,
"passthru": false,
"label": "Reset WiFi ESP32",
"tooltip": "Klik untuk reset koneksi WiFi",
"color": "",
"bgcolor": "#ff6666",
"className": "",
"icon": "fa-refresh",
"payload": "reset",
"payloadType": "str",
"topic": "ac/resetwifi",
"topicType": "str",
"x": 190,
"y": 340,
"wires": [
[
"456c3c5f815e9edf"
]
]
},
{
"id": "f043c813369ccfc7",
"type": "ui_slider",
"z": "dd166f1fb1414356",
"name": "Suhu",
"label": "Temperature",
"tooltip": "",
"group": "8bd4a1c79f424e45",
"order": 3,
"width": 6,
"height": 1,
"passthru": true,
"outs": "end",
"topic": "ac/temp",
"topicType": "str",
"min": 18,
"max": 30,
"step": 1,
"className": "",
"x": 210,
"y": 120,
"wires": [
[
"e26985a773758b5b",
"fed55988a21fe62d"
]
]
},
{
"id": "fed55988a21fe62d",
"type": "function",
"z": "dd166f1fb1414356",
"name": "Send to Firebase and UI Table",
"func": "let topic = msg.topic\nlet value = msg.payload;\nlet timestamp = Date.now();\nlet waktu = new Date().toLocaleString(\"id-ID\");\n\n// Ambil status terakhir AC\nlet lastState = flow.get(\"statusAC\") || {\n power: null,\n temp: null,\n swing: null,\n fan: null\n};\n\n// Perbarui status sesuai topik\nif (topic === \"ac/power\") {\n lastState.power = value;\n} else if (topic === \"ac/temp\") {\n lastState.temp = value;\n} else if (topic === \"ac/swing\") {\n lastState.swing = value;\n} else if (topic === \"ac/fan\") {\n lastState.fan = value;\n} else if (topic === \"reset\") {\n // Reset semua\n flow.set(\"statusAC\", null);\n flow.set(\"riwayatAC\", []);\n\n let msgFirebase = {\n payload: null,\n topic: \"ac/power\"\n };\n let msgTable = {\n payload: []\n };\n return [msgFirebase, msgTable];\n} else {\n return [null, null]; // Topik tidak dikenal\n}\n\n// Simpan ulang status terakhir\nflow.set(\"statusAC\", lastState);\n\n// Buat entry riwayat lengkap\nlet dataBaru = {\n timestamp,\n waktu,\n power: lastState.power,\n temp: lastState.temp,\n swing: lastState.swing,\n fan: lastState.fan\n};\n\n// Simpan ke array riwayat\nlet history = flow.get(\"riwayatAC\") || [];\nhistory.push(dataBaru);\n\n\nflow.set(\"riwayatAC\", history);\n\nlet msgFirebase = {\n payload: dataBaru,\n topic: `ac_${timestamp}` // untuk ID dokumen unik\n};\n\n// Kirim ke Firebase\n// let msgFirebase = {\n// payload: {\n// [timestamp]: dataBaru\n// },\n// topic: \"ac/power\"\n// };\n\n// Kirim ke UI Table\n// riwayatAC:\n// [\"on\", \"off\"]\n\nlet msgTable = {\n payload: history\n};\n\nreturn [msgFirebase, msgTable];",
"outputs": 2,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 550,
"y": 180,
"wires": [
[
"afcf08f2c2555abf"
],
[
"9776cfb20df1c2e3"
]
]
},
{
"id": "f1f65fb261894e0d",
"type": "ui_button",
"z": "dd166f1fb1414356",
"name": "Button Reset",
"group": "f893e31c484e44b1",
"order": 2,
"width": 0,
"height": 0,
"passthru": false,
"label": "Reset History",
"tooltip": "",
"color": "",
"bgcolor": "#ff6666",
"className": "",
"icon": "delete",
"payload": "",
"payloadType": "str",
"topic": "reset",
"topicType": "str",
"x": 450,
"y": 280,
"wires": [
[
"fed55988a21fe62d"
]
]
},
{
"id": "9776cfb20df1c2e3",
"type": "ui_template",
"z": "dd166f1fb1414356",
"group": "f893e31c484e44b1",
"name": "Table Riwayat AC",
"order": 1,
"width": "6",
"height": "7",
"format": "<div style=\"width: 100%; height: 100%; overflow: auto\">\n <table style=\"width: 100%; border-collapse: collapse; font-size: 14px\">\n <thead>\n <tr style=\"background: #eeeeee\">\n <th>Waktu</th>\n <th>Power</th>\n <th>Temp</th>\n <th>Swing</th>\n <th>Fan</th>\n </tr>\n </thead>\n <tbody id=\"ac-table-body\">\n </tbody>\n </table>\n</div>\n\n<script>\n(function(scope) {\n scope.$watch('msg', function(msg) {\n if (msg && msg.payload) {\n const tbody = document.getElementById('ac-table-body');\n // Jika payload merupakan array, berarti refresh dari awal\n if (Array.isArray(msg.payload)) {\n tbody.innerHTML = '';\n msg.payload.forEach(function(item) {\n let row = `\n <tr>\n <td>${item.waktu || ''}</td>\n <td>${item.power || ''}</td>\n <td>${item.temp || ''}</td>\n <td>${item.swing || ''}</td>\n <td>${item.fan || ''}</td>\n </tr>`;\n tbody.innerHTML += row;\n });\n } else {\n // Jika bukan array, berarti single row, append saja\n let item = msg.payload;\n let row = `\n <tr>\n <td>${item.waktu || ''}</td>\n <td>${item.power || ''}</td>\n <td>${item.temp || ''}</td>\n <td>${item.swing || ''}</td>\n <td>${item.fan || ''}</td>\n </tr>`;\n tbody.innerHTML += row;\n }\n }\n });\n})(scope);\n</script>\n",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
"className": "",
"x": 850,
"y": 180,
"wires": [
[]
]
},
{
"id": "456c3c5f815e9edf",
"type": "mqtt out",
"z": "dd166f1fb1414356",
"name": "MQTT RESET WIFI",
"topic": "ac/resetwifi",
"qos": "",
"retain": "",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "9fbca1958dadbcd5",
"x": 470,
"y": 340,
"wires": []
},
{
"id": "77f6c3bcdb24cb4b",
"type": "function",
"z": "dd166f1fb1414356",
"name": "Builder Cron dari Form",
"func": "let waktu = msg.payload.time;\nlet aksi = msg.payload.action;\nlet hari = msg.payload.hari || \"\";\nlet tanggal = msg.payload.tanggal || \"\";\n\n// Deteksi dan ubah waktu ISO (jika input adalah string format ISO)\nif (typeof waktu === 'string' && waktu.includes(\"T\")) {\n try {\n let t = new Date(waktu);\n if (isNaN(t.getTime())) throw \"Invalid time\";\n let jam = t.getHours().toString().padStart(2, \"0\");\n let menit = t.getMinutes().toString().padStart(2, \"0\");\n waktu = `${jam}:${menit}`;\n } catch (e) {\n node.error(\"⛔ Format waktu ISO tidak valid.\");\n return null;\n }\n}\n\n// Pastikan waktu sudah dalam format HH:MM\nif (!/^\\d{2}:\\d{2}$/.test(waktu)) {\n node.error(\"⛔ Format waktu tidak valid. Harus HH:MM.\");\n return null;\n}\n\nlet [jam, menit] = waktu.split(\":\").map(Number);\nif (isNaN(jam) || isNaN(menit)) {\n node.error(\"Jam atau menit bukan angka.\");\n return null;\n}\n\nlet id = `jadwal_${jam}_${menit}_${aksi}_${Date.now()}`;\n\nlet dayMap = {\n \"Minggu\": 0,\n \"Senin\": 1,\n \"Selasa\": 2,\n \"Rabu\": 3,\n \"Kamis\": 4,\n \"Jumat\": 5,\n \"Sabtu\": 6\n};\n\nlet expression = \"\";\n\nif (hari !== \"\") {\n let hariList = hari.split(\",\").map(h => dayMap[h.trim()]);\n expression = `0 ${menit} ${jam} * * ${hariList.join(\",\")}`;\n} else if (tanggal !== \"\") {\n let dt = new Date(tanggal);\n if (isNaN(dt)) {\n node.error(\"Tanggal tidak valid.\");\n return null;\n }\n let day = dt.getDate();\n let month = dt.getMonth() + 1;\n let year = dt.getFullYear();\n expression = `0 ${menit} ${jam} ${day} ${month} * ${year}`;\n} else {\n expression = `0 ${menit} ${jam} * * *`;\n}\n\nmsg.payload = {\ncommand: \"add\",\nname: id,\n expression: expression,\n payload: {\n topic: \"ac/power\",\n payload: aksi // <- pastikan ini ada\n },\n payloadType: \"default\",\n timezone: \"Asia/Jakarta\"\n};\n\n// Simpan data jadwal ke dalam msg untuk disimpan ke tabel UI\nmsg.jadwalInfo = {\n id: id,\n waktu: waktu,\n aksi: aksi,\n hari: hari,\n tanggal: tanggal,\n expression: expression\n};\n\nreturn msg;",
"outputs": 1,
"timeout": "",
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 440,
"y": 700,
"wires": [
[
"5de18712a68b9fad",
"10101b10536635de"
]
]
},
{
"id": "dc609343abd6df63",
"type": "mqtt out",
"z": "dd166f1fb1414356",
"name": "MQTT KONTROL AC",
"topic": "ac/power",
"qos": "0",
"retain": "false",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "9fbca1958dadbcd5",
"x": 1100,
"y": 700,
"wires": []
},
{
"id": "549d0a5c93b2f529",
"type": "function",
"z": "dd166f1fb1414356",
"name": "function 1",
"func": "if (msg.payload && msg.payload.config && msg.payload.config.payload) {\n msg.topic = msg.payload.config.payload.topic;\n msg.payload = msg.payload.config.payload.payload;\n return msg;\n// } else {\n// node.warn(\"⛔ Payload cron tidak sesuai.\");\n// return null;\n}",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 880,
"y": 700,
"wires": [
[
"dc609343abd6df63",
"fed55988a21fe62d"
]
]
},
{
"id": "56ae79c457a7ed78",
"type": "ui_template",
"z": "dd166f1fb1414356",
"group": "37291ecad70d0e70",
"name": "Form Jadwal AC",
"order": 1,
"width": 6,
"height": "10",
"format": "<div layout=\"column\" style=\"padding: 10px;\">\n <h3><b> Buat Jadwal Kontrol AC</b></h3>\n\n <div style=\"margin-top:10px;\">\n <label><strong>📅 Tanggal:</strong></label><br>\n <input type='date' ng-model='data.tanggal'>\n </div>\n\n <div style=\"margin-top:10px;\">\n <label><strong>📆 Pilih Hari (berulang):</strong></label><br>\n <label ng-repeat=\"h in ['Senin','Selasa','Rabu','Kamis','Jumat','Sabtu','Minggu']\">\n <input type='checkbox' ng-model='data[h]'> {{h}}\n </label>\n </div>\n\n <div style=\"margin-top:10px;\">\n <label><strong>🕒 Jam:</strong></label><br>\n <input type='time' ng-model='data.time' required>\n </div>\n\n <div style=\"margin-top:10px;\">\n <label><strong>⚙️ Status</strong></label><br>\n <select ng-model='data.action' ng-options=\"opt for opt in ['on','off']\" required>\n <option value=\"\">-- Pilih on/off --</option>\n </select>\n </div>\n\n <div style=\"margin-top:15px;\">\n <md-button class='md-raised md-primary' ng-click='submitJadwal()'>\n Tambah Jadwal\n </md-button>\n </div>\n</div>\n\n<script>\n (function(scope) {\n scope.data = scope.data || {};\n\n scope.submitJadwal = function() {\n const data = scope.data;\n if (!data.time || !data.action) {\n alert(\"⛔ Harap isi jam dan status terlebih dahulu!\");\n return;\n }\n\n const hariList = [\"Senin\", \"Selasa\", \"Rabu\", \"Kamis\", \"Jumat\", \"Sabtu\", \"Minggu\"];\n const hariDipilih = hariList.filter(h => data[h]);\n\n const hasil = {\n tanggal: data.tanggal || \"\",\n hari: hariDipilih.join(\", \"),\n time: data.time,\n action: data.action.toLowerCase()\n };\n\n scope.send({ payload: hasil });\n scope.data = {}; // reset\n };\n })(scope);\n</script>\n",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 180,
"y": 700,
"wires": [
[
"77f6c3bcdb24cb4b"
]
]
},
{
"id": "018cdc13cbbcfb25",
"type": "ui_template",
"z": "dd166f1fb1414356",
"group": "9ed1eb381b037864",
"name": "",
"order": 1,
"width": "16",
"height": "10",
"format": "<style type=\"text/css\">\n .t1 {\n border-collapse: collapse;\n border-spacing: 0;\n border-color: #ccc;\n }\n\n .t1 td {\n font-family: Arial, sans-serif;\n font-size: 14px;\n padding: 8px 3px;\n border-style: solid;\n border-width: 0px;\n overflow: hidden;\n word-break: normal;\n border-top-width: 1px;\n border-bottom-width: 1px;\n border-color: #ccc;\n color: #333;\n background-color: #fff;\n }\n\n .t1 th {\n font-family: Arial, sans-serif;\n font-size: 14px;\n font-weight: normal;\n padding: 10px 5px;\n border-style: solid;\n border-width: 1px;\n overflow: hidden;\n word-break: normal;\n border-color: #ccc;\n color: #333;\n background-color: #f0f0f0;\n }\n\n .t1 .t1-judul {\n background-color: #000000;\n color: #ffffff;\n text-align: left;\n vertical-align: top\n }\n\n .t1 .t1-header {\n background-color: #c0c0c0;\n font-size: small;\n text-align: left;\n vertical-align: top\n }\n\n .t1 .t1-header-center {\n background-color: #c0c0c0;\n font-size: small;\n text-align: center;\n vertical-align: top\n }\n\n .t1 .t1-data {\n font-size: small;\n text-align: left;\n vertical-align: top\n }\n\n .t1 .t1-center {\n font-size: small;\n text-align: center;\n vertical-align: middle\n }\n</style>\n\n<table class=\"t1\">\n <thead>\n <tr>\n <th class=\"t1-judul\" colspan=\"10\">Jadwal AC</th>\n </tr>\n <tr>\n <th class=\"t1-header\">ID</th>\n <th class=\"t1-header\">Waktu</th>\n <th class=\"t1-header\">Hari</th>\n <th class=\"t1-header\">Status</th>\n <th class=\"t1-header\">Tanggal</th>\n <th class=\"t1-header\">Expression</th>\n <th class=\"t1-header\">State</th>\n <th class=\"t1-header-center\">Delete</th>\n <th class=\"t1-header-center\">Pause</th>\n <th class=\"t1-header-center\">Resume</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat=\"item in msg.payload\">\n <td class=\"t1-data\">{{item.id}}</td>\n <td class=\"t1-data\">{{item.waktu}}</td>\n <td class=\"t1-data\">{{item.hari}}</td>\n <td class=\"t1-data\">{{item.aksi}}</td>\n <td class=\"t1-data\">{{item.tanggal}}</td>\n <td class=\"t1-data\">{{item.expression}}</td>\n <td class=\"t1-data\">{{item.state}}</td>\n <td class=\"t1-center\">\n <md-button class=\"md-raised\"\n ng-click=\"send([[{payload:{command:'remove', name: item.id}}, {payload:{command:'list-all'}}]]);\">\n <i class=\"fa fa-trash\"></i>\n </md-button>\n </td>\n <td class=\"t1-center\">\n <md-button class=\"button\"\n ng-click=\"send([[{payload:{command:'pause', name: item.id}}, {payload:{command:'list-all'}}]]);\">\n <i class=\"fa fa-pause\"></i>\n </md-button>\n </td>\n <td class=\"t1-center\">\n <md-button class=\"button\"\n ng-click=\"send([[{payload:{command:'start', name: item.id}}, {payload:{command:'list-all'}}]]);\">\n <i class=\"fa fa-play\"></i>\n </md-button>\n </td>\n </tr>\n </tbody>\n</table>",
"storeOutMessages": false,
"fwdInMessages": false,
"resendOnRefresh": false,
"templateScope": "local",
"className": "",
"x": 840,
"y": 820,
"wires": [
[
"fde5a22acf4ec433"
]
]
},
{
"id": "7db5bda8a2122f27",
"type": "function",
"z": "dd166f1fb1414356",
"name": "Make table data",
"func": "let raw = msg.payload.result || [];\nif(!msg.payload || !msg.payload.result || !msg.payload.result.length){\n msg.payload = [];\n return msg;\n}\n\nmsg.payload = msg.payload.result;\n\nlet dayMapReverse = {\n \"0\": \"Minggu\",\n \"1\": \"Senin\",\n \"2\": \"Selasa\",\n \"3\": \"Rabu\",\n \"4\": \"Kamis\",\n \"5\": \"Jumat\",\n \"6\": \"Sabtu\"\n};\n\nlet jadwals = raw.map(item => {\n let expr = item.config.expression.split(\" \");\n let menit = expr[1]?.padStart(2, '0') || \"00\";\n let jam = expr[2]?.padStart(2, '0') || \"00\";\n let tanggal = \"-\";\n let hari = \"-\";\n let tahun = expr[6] !== undefined ? expr[6] : \"\";\n\n if (expr[3] !== \"*\" && expr[4] !== \"*\") {\n // By tanggal\n tanggal = `${expr[3].padStart(2, '0')}/${expr[4].padStart(2, '0')}`;\n if (tahun) tanggal += `/${tahun}`;\n } else if (expr[5] !== \"*\") {\n // By hari (misal: 1,3,5)\n hari = expr[5]\n .split(\",\")\n .map(h => dayMapReverse[h.trim()] || h)\n .join(\", \");\n }\n\n return {\n id: item.config.name,\n waktu: `${jam}:${menit}`,\n hari: hari,\n aksi: item.config.payload?.payload || \"-\", // ambil dari payload.payload\n tanggal: tanggal,\n expression: item.config.expression,\n state: item.status?.isRunning ? \"Running\" : \"Paused\"\n };\n});\n\nmsg.payload = jadwals;\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 660,
"y": 820,
"wires": [
[
"018cdc13cbbcfb25"
]
]
},
{
"id": "10101b10536635de",
"type": "ui_button",
"z": "dd166f1fb1414356",
"name": "Refresh",
"group": "9ed1eb381b037864",
"order": 6,
"width": 0,
"height": 0,
"passthru": true,
"label": "",
"tooltip": "",
"color": "",
"bgcolor": "",
"className": "",
"icon": "fa-refresh",
"payload": "{\"command\":\"list-all\"}",
"payloadType": "json",
"topic": "",
"topicType": "str",
"x": 540,
"y": 620,
"wires": [
[
"5de18712a68b9fad"
]
]
},
{
"id": "fde5a22acf4ec433",
"type": "link out",
"z": "dd166f1fb1414356",
"name": "link out 1",
"mode": "link",
"links": [
"a7a4af9974c0b0b7"
],
"x": 955,
"y": 820,
"wires": []
},
{
"id": "a7a4af9974c0b0b7",
"type": "link in",
"z": "dd166f1fb1414356",
"name": "link in 1",
"links": [
"fde5a22acf4ec433"
],
"x": 715,
"y": 620,
"wires": [
[
"5de18712a68b9fad"
]
]
},
{
"id": "5de18712a68b9fad",
"type": "cronplus",
"z": "dd166f1fb1414356",
"name": "cron plus",
"outputField": "payload",
"timeZone": "Asia/Jakarta",
"storeName": "",
"commandResponseMsgOutput": "output1",
"defaultLocation": "",
"defaultLocationType": "default",
"outputs": 1,
"options": [],
"x": 680,
"y": 700,
"wires": [
[
"549d0a5c93b2f529",
"7db5bda8a2122f27"
]
]
},
{
"id": "0d32caa7f8235bfc",
"type": "function",
"z": "dd166f1fb1414356",
"name": "function 3",
"func": "let timestamp = new Date().getTime().toString();\n\nmsg.payload = {\n suhu: 18,\n kelembapan: 70,\n waktu: new Date().toLocaleString(\"id-ID\")\n};\n\nmsg.document = timestamp;\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1220,
"y": 240,
"wires": [
[
"6e5888c6866aa74e"
]
]
},
{
"id": "6e5888c6866aa74e",
"type": "Firestore out",
"z": "dd166f1fb1414356",
"name": "Kirim ke Firestore",
"collection": "sensor_dht22",
"document": "msg.document",
"operation": "set",
"admin": "6a37fe7abbfec3e2",
"eject": false,
"x": 1450,
"y": 240,
"wires": [
[]
]
},
{
"id": "c0b3097096ea1936",
"type": "inject",
"z": "dd166f1fb1414356",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 1060,
"y": 240,
"wires": [
[
"0d32caa7f8235bfc"
]
]
},
{
"id": "afcf08f2c2555abf",
"type": "Firestore out",
"z": "dd166f1fb1414356",
"name": "Kirim ke Firestore",
"collection": "KONTROL AC",
"document": "{{topic}}",
"operation": "set",
"admin": "6a37fe7abbfec3e2",
"eject": false,
"x": 830,
"y": 80,
"wires": [
[]
]
},
{
"id": "4061d92b9545a878",
"type": "Firestore out",
"z": "dd166f1fb1414356",
"name": "Kirim ke Firestore",
"collection": "Sensor DHT22",
"document": "{{topic}}",
"operation": "set",
"admin": "6a37fe7abbfec3e2",
"eject": false,
"x": 1450,
"y": 460,
"wires": [
[]
]
},
{
"id": "4daa938f22269682",
"type": "ui_template",
"z": "dd166f1fb1414356",
"group": "565ebc18e3d1af6f",
"name": "",
"order": 3,
"width": 0,
"height": 0,
"format": "<div style=\"font-size:17px; font-weight:bold; text-align:center;\n padding:10px; border-radius:5px;\n color: {{msg.payload.status === 'sesuai' ? 'green' : 'red'}};\">\n Suhu {{msg.payload.status === 'sesuai' ? 'Sudah Sesuai ✅' : 'Belum Sesuai ⚠️'}}\n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 1160,
"y": 540,
"wires": [
[]
]
},
{
"id": "1c4be2044b325aeb",
"type": "function",
"z": "dd166f1fb1414356",
"name": "function 4",
"func": "let timestamp = new Date().toISOString();\n\n// Ambil data sesuai field sebenarnya\nlet suhu = msg.payload.suhu;\nlet kelembapan = msg.payload.kelembapan;\nlet status = msg.payload.status;\n\nif (suhu === undefined || kelembapan === undefined) {\n node.error(\"Data suhu atau kelembapan tidak ditemukan\", msg);\n return null;\n}\n\n// Format untuk Firestore\nmsg.topic = timestamp;\nmsg.payload = {\n suhu: suhu,\n kelembapan: kelembapan,\n status: status,\n waktu: new Date().toLocaleString(\"id-ID\")\n};\n\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1260,
"y": 460,
"wires": [
[
"4061d92b9545a878"
]
]
},
{
"id": "75da73cb7683e5d2",
"type": "ui_spacer",
"z": "dd166f1fb1414356",
"name": "spacer",
"group": "8bd4a1c79f424e45",
"order": 2,
"width": 3,
"height": 1
},
{
"id": "8bd4a1c79f424e45",
"type": "ui_group",
"name": "KONTROL AC",
"tab": "6d66f5b86ad8ba34",
"order": 1,
"disp": true,
"width": 6,
"collapse": false,
"className": ""
},
{
"id": "9fbca1958dadbcd5",
"type": "mqtt-broker",
"name": "HiveMQ Private",
"broker": "",
"port": "",
"tls": "",
"clientid": "",
"autoConnect": true,
"usetls": true,
"protocolVersion": 4,
"keepalive": 60,
"cleansession": true,
"autoUnsubscribe": true,
"birthTopic": "",
"birthQos": "0",
"birthRetain": "false",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closeRetain": "false",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willRetain": "false",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
},
{
"id": "565ebc18e3d1af6f",
"type": "ui_group",
"name": "SUHU DAN KELEMBAPAN",
"tab": "6d66f5b86ad8ba34",
"order": 2,
"disp": true,
"width": 6,
"collapse": false,
"className": ""
},
{
"id": "f893e31c484e44b1",
"type": "ui_group",
"name": "History",
"tab": "6d66f5b86ad8ba34",
"order": 3,
"disp": true,
"width": 6,
"collapse": false,
"className": ""
},
{
"id": "37291ecad70d0e70",
"type": "ui_group",
"name": "ATUR JADWAL",
"tab": "39df619663f80ba7",
"order": 1,
"disp": true,
"width": 6,
"collapse": false,
"className": ""
},
{
"id": "9ed1eb381b037864",
"type": "ui_group",
"name": "DAFTAR JADWAL",
"tab": "39df619663f80ba7",
"order": 2,
"disp": true,
"width": "16",
"collapse": false,
"className": ""
},
{
"id": "6a37fe7abbfec3e2",
"type": "firebase admin",
"name": "FirebaseAC"
},
{
"id": "6d66f5b86ad8ba34",
"type": "ui_tab",
"name": "CONTROL DAN MONITORING AC",
"icon": "dashboard",
"order": 1,
"disabled": false,
"hidden": false
},
{
"id": "39df619663f80ba7",
"type": "ui_tab",
"name": "PENJADWALAN",
"icon": "dashboard",
"disabled": false,
"hidden": false
}
]