858 lines
33 KiB
JSON
858 lines
33 KiB
JSON
[
|
||
{
|
||
"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
|
||
}
|
||
] |