Wokwi ESP32 + Node-Red Dashboard UI Template + AngularJS
Node-Red程式
[{"id":"6fee776ed1432473","type":"mqtt in","z":"3df70b2bf910d4a0","name":"","topic":"alex9ufo/temphumi","qos":"1","datatype":"auto-detect","broker":"192c2b20bef1e71a","nl":false,"rap":true,"rh":0,"inputs":0,"x":110,"y":180,"wires":[["a4a10947ee98d61c"]]},{"id":"f0cd3b565c12d25e","type":"mqtt in","z":"3df70b2bf910d4a0","name":"","topic":"alex9ufo/ledstatus","qos":"1","datatype":"auto-detect","broker":"192c2b20bef1e71a","nl":false,"rap":true,"rh":0,"inputs":0,"x":110,"y":240,"wires":[["a4a10947ee98d61c"]]},{"id":"a4a10947ee98d61c","type":"json","z":"3df70b2bf910d4a0","name":"","property":"payload","action":"","pretty":false,"x":290,"y":200,"wires":[["47079334473fc103","080bc85b17a642e3"]]},{"id":"51c209dc2b2b3dbd","type":"mqtt out","z":"3df70b2bf910d4a0","name":"","topic":"alex9ufo/ledcontrol","qos":"1","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"192c2b20bef1e71a","x":810,"y":180,"wires":[]},{"id":"47079334473fc103","type":"debug","z":"3df70b2bf910d4a0","name":"debug 376","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":450,"y":160,"wires":[]},{"id":"ae5ae9dcd87cd424","type":"debug","z":"3df70b2bf910d4a0","name":"debug 377","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":790,"y":220,"wires":[]},{"id":"080bc85b17a642e3","type":"function","z":"3df70b2bf910d4a0","name":"(溫度判斷)","func":"// 確保 payload 是物件\nif (typeof msg.payload === \"string\") {\n msg.payload = JSON.parse(msg.payload);\n}\n\nvar temp = msg.payload.temp;\n\n// 邏輯判斷\nif (temp > 70) {\n msg.status = \"危險:極高溫!\";\n msg.color = \"red\";\n} else if (temp > 30) {\n msg.status = \"警告:溫度偏高\";\n msg.color = \"orange\";\n} else {\n msg.status = \"溫度正常\";\n msg.color = \"green\";\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":200,"wires":[["337a5f79ec4f3683"]]},{"id":"337a5f79ec4f3683","type":"ui_template","z":"3df70b2bf910d4a0","group":"63fff320de16e3a7","name":"","order":0,"width":0,"height":0,"format":"<div layout=\"column\" style=\"background: #2b2b2b; color: #eee; padding: 15px; border-radius: 10px;\">\n\n <div layout=\"row\" layout-align=\"space-around center\"\n style=\"margin-bottom: 15px; background: #1a1a1a; padding: 10px; border-radius: 8px;\">\n <div style=\"text-align: center;\">\n <div style=\"font-size: 0.8em; color: #888;\">TEMPERATURE</div>\n <div style=\"font-size: 1.8em; color: #ff9800;\">{{status.temp ||\n '--'}}<span style=\"font-size: 0.6em;\">°C</span></div>\n </div>\n <div style=\"text-align: center;\">\n <div style=\"font-size: 0.8em; color: #888;\">HUMIDITY</div>\n <div style=\"font-size: 1.8em; color: #03a9f4;\">{{status.humi ||\n '--'}}<span style=\"font-size: 0.6em;\">%</span></div>\n </div>\n </div>\n\n <div ng-repeat=\"i in [1,2,3,4]\" layout=\"row\" layout-align=\"space-between center\"\n style=\"padding: 8px; border-bottom: 1px solid #444;\">\n\n <div style=\"font-weight: bold; width: 50px;\">LED {{i}}</div>\n\n <div ng-style=\"{'background-color': (status['led'+i] == 1 || status['led'+i] == '1') ? '#00FF00' : '#FF0000'}\"\n style=\"width: 18px; height: 18px; border-radius: 50%; box-shadow: 0 0 10px rgba(0,0,0,0.5); transition: 0.3s;\">\n </div>\n\n <div layout=\"row\">\n <md-button class=\"md-raised\" style=\"background: #2e7d32; color: white; min-width: 45px; margin: 0 4px;\"\n ng-click=\"ctrl(i, 1)\">ON</md-button>\n <md-button class=\"md-raised\" style=\"background: #c62828; color: white; min-width: 45px; margin: 0 4px;\"\n ng-click=\"ctrl(i, 0)\">OFF</md-button>\n </div>\n </div>\n</div>\n\n<script>\n (function(scope) {\n // 初始化狀態容器\n scope.status = { temp: '--', humi: '--', led1: 0, led2: 0, led3: 0, led4: 0 };\n\n // 監聽傳入的 msg\n scope.$watch('msg', function(msg) {\n if (!msg || !msg.payload) return;\n\n var newData = msg.payload;\n\n // 確保資料是物件格式\n if (typeof newData === 'string') {\n try {\n newData = JSON.parse(newData);\n } catch (e) { return; }\n }\n\n // 使用 Angular 的方式合併資料並強制更新畫面\n scope.$evalAsync(function() {\n // 將新資料合併到 status 物件中\n angular.extend(scope.status, newData);\n });\n });\n\n // 發送控制指令\n scope.ctrl = function(id, state) {\n scope.send({\n topic: \"alex9ufo/ledcontrol\",\n payload: { \"id\": id, \"state\": state }\n });\n };\n })(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":620,"y":180,"wires":[["51c209dc2b2b3dbd","ae5ae9dcd87cd424"]]},{"id":"192c2b20bef1e71a","type":"mqtt-broker","name":"mqttgo","broker":"mqttgo.io","port":"1883","clientid":"","autoConnect":true,"usetls":false,"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":"63fff320de16e3a7","type":"ui_group","name":"Dashboard UI Template","tab":"8c00e350715570d8","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"8c00e350715570d8","type":"ui_tab","name":"2026-ex6","icon":"dashboard","disabled":false,"hidden":false}]
Function 節點
template 節點 (AngularJS)





沒有留言:
張貼留言