2020年9月3日 星期四

Node-RED

               Node-RED

源自於 https://xanxusvervr.blogspot.com/2017/07/node-red.html
啟動Node-RED
$ node-red
  1. 11 Jul 20:39:32 - [info]
  2.  
  3. Welcome to Node-RED
  4. ===================
  5.  
  6. 11 Jul 20:39:32 - [info] Node-RED version: v0.16.2
  7. 11 Jul 20:39:32 - [info] Node.js version: v6.11.0
  8. 11 Jul 20:39:32 - [info] Darwin 16.6.0 x64 LE
  9. 11 Jul 20:39:33 - [info] Loading palette nodes
  10. 11 Jul 20:39:33 - [warn] ------------------------------------------------------
  11. 11 Jul 20:39:33 - [warn] [rpi-gpio] Info : Ignoring Raspberry Pi specific node
  12. 11 Jul 20:39:33 - [warn] ------------------------------------------------------
  13. 11 Jul 20:39:33 - [info] Settings file : /Users/xanxus/.node-red/settings.js
  14. 11 Jul 20:39:33 - [info] User directory : /Users/xanxus/.node-red
  15. 11 Jul 20:39:33 - [info] Flows file : /Users/xanxus/.node-red/flows_XanxusMacde-MacBook-Pro.local.json
  16. 11 Jul 20:39:33 - [info] Server now running at http://127.0.0.1:1880/
  17. 11 Jul 20:39:33 - [info] Starting flows
  18. 11 Jul 20:39:33 - [info] Started flows

http://127.0.0.1:1880/ 就是Node-RED平台的網址



Note:

  1. 如果Node-RED不是裝在全域的話,執行方式似乎也有所差異[1]
  2. Node-RED預設會儲存使用者資料在家目錄底下的.node-red,--userDir可作相關的操作
  3. 當在Raspberry Pi or BeagleBone Black等限制記憶體的裝置上運作時,或許需要把參數傳給node.js處理 [1]
  4. 在開機時啟動Node-RED,在Windows上可能要用NSSM的方案 [1]

查看Node-RED版本:
$ node-red --help

更新or升級Node-RED [2]:
$ sudo npm cache clean (npm5.x版貌似不支援)
$ sudo npm update -g --unsafe-perm node-red

可以額外安裝新的Node-RED節點 [3]

有兩種方式安裝:
第一種、直接在Node-RED面板上安裝
點選面板右邊的菜單選單中的 Manage palette,切到Install這個tag,可以直接在這搜尋你要的套件,這裡會搜尋到Node-RED Library網站上有的套件,如果沒有在網站上的就搜尋不到
或者可直接在Node-RED Library網站直接搜尋,並直接用npm指令安裝,如:
$ npm install node-red-contrib-json-schema-validator
第二種、git clone安裝
假設我要OM2M,但官方函式庫沒有,如果github上有的話,可以clone下來在安裝
先到https://github.com/themaco/node-red-contrib-om2m clone下來
放到你的函式庫資料夾,並且到這套件的目錄位置,並安裝:
$ sudo npm install -g



Node-RED Library https://flows.nodered.org/
npm上的Node-REDpackage https://www.npmjs.com/browse/keyword/node-red
可到上面兩個連結找還有哪些額外的節點函式庫

建置一個簡單的Flow [4]


  1. 拉一個Inject節點,右鍵點擊節點,Repeat設Interval between times,every選5minutes,好了按Done 
  2. 再拉一個HttpRequest節點,與前面節點連起來,URL填http://realtimeweb-prod.nationalgrid.com/SystemData.aspx
  3. 再拉一個function節點,與前面節點連起來,可以在這個節點裡面寫js程式碼,貼上以下程式碼:
    1. // does a simple text extract parse of the http output to provide an
    2. // object containing the uk power demand, frequency and time
    3.  
    4. if (~msg.payload.indexOf('<span')) {
    5. var dem = msg.payload.split('Demand:')[1].split("MW")[0];
    6. var fre = msg.payload.split('Frequency:')[1].split("Hz")[0];
    7.  
    8. msg.payload = {};
    9. msg.payload.demand = parseInt(dem.split(">")[1].split("<")[0]);
    10. msg.payload.frequency = parseFloat(fre.split(">")[1].split("<")[0]);
    11.  
    12. msg2 = {};
    13. msg2.payload = (msg.payload.frequency >= 50) ? true : false;
    14.  
    15. return [msg,msg2];
    16. }
    17. return null;
    最後Outputs選2
  4. 最後拉一個debug節點,與前一個節點連起來,因為剛剛Output是2,因此要連兩條線。
  5. 點Deploy
  6. 點Inject節點前面的小區塊,就會下請求了。
  7. 回應結果在右邊的debug視窗。

Node-RED的一些系統設定  [5]

如在跑一些獨立的應用程式時,一些參數的屬性設定會從settings.js檔案讀取,貌似此檔案會根據放的地方來決定執行的順序。

也可以設定一些系統的參數,像port、host IP等等

還可以更換佈景主題

Node-RED預設沒有被保護,只要知道Node-RED的IP和網址的人都可以隨意修改節點與部署。[6]

因此Node-RED也有提供一些安全上的設定。

Node-RED的Log的功能

可以在settings.js設定log的一些功能與屬性
Note:

  1. 可以用自行定義的logger模組,來把輸出發送到其他位置。

可以用指令模式來管理Node-RED [7]

但需要安裝node-red-admin

Node-RED的Function Node [8]


Function Node有一個重要也是最主要的物件msg
msg底下有很多屬性如msg.payload
而msg.payload裡面放的東西其實就像http的body
msg.url放的就是你要請求的網址
msg.headers是你請求的標頭

輸出也可以是陣列:
  1. [{"id":"ea46f50f.753b08","type":"inject","z":"eb44de0d.fcac1","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":186,"y":274,"wires":[["480852e1.561e6c"]]},{"id":"480852e1.561e6c","type":"function","z":"eb44de0d.fcac1","name":"","func":"msg.url = \"https://api.api.ai/v1/query?v=20150910\";\nmsg.headers = {\n \"Authorization\": \"Bearer 2b0ed04782064644b8066768bbda2fa5\",\n\"Content-Type\": \"application/json; charset=utf-8\"\n\n};\nmsg.payload = {\n \"query\": [\n \"我想去帥勁影印\"\n ],\n \"lang\": \"zh-cn\",\n \"sessionId\": \"1234567890\"\n};\n\nvar output = [msg,null];\n\nreturn output;","outputs":"2","noerr":0,"x":352,"y":274,"wires":[["395a11ef.5f572e"],["f825cd3e.6c83b"]]},{"id":"f825cd3e.6c83b","type":"debug","z":"eb44de0d.fcac1","name":"","active":true,"console":"false","complete":"payload","x":528,"y":300,"wires":[]},{"id":"395a11ef.5f572e","type":"debug","z":"eb44de0d.fcac1","name":"","active":true,"console":"false","complete":"payload","x":539,"y":229,"wires":[]}]
先把這段Flow匯入,打開Function Node看程式碼
var output = [msg,null];
return output;
因為我output是一個陣列,因此我這個節點的輸出要拉兩條線,分別輸出到各別的Node,此例是上面那個節點觸發,而節點輸出的內容就是msg.payload,就是剛剛寫在裡面的

對API.AI下請求,並取得回應:
  1. [{"id":"ea46f50f.753b08","type":"inject","z":"eb44de0d.fcac1","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":186,"y":274,"wires":[["480852e1.561e6c"]]},{"id":"480852e1.561e6c","type":"function","z":"eb44de0d.fcac1","name":"","func":"msg.url = \"https://api.api.ai/v1/query?v=20150910\";\nmsg.headers = {\n \"Authorization\": \"Bearer 2b0ed04782064644b8066768bbda2fa5\",\n\"Content-Type\": \"application/json; charset=utf-8\"\n\n};\nmsg.payload = {\n \"query\": [\n \"我想去帥勁影印\"\n ],\n \"lang\": \"zh-cn\",\n \"sessionId\": \"1234567890\"\n};\n\n\n\nreturn msg;","outputs":"2","noerr":0,"x":352,"y":274,"wires":[["97235edf.7a0cf"],[]]},{"id":"395a11ef.5f572e","type":"debug","z":"eb44de0d.fcac1","name":"","active":true,"console":"false","complete":"payload","x":718,"y":268,"wires":[]},{"id":"97235edf.7a0cf","type":"http request","z":"eb44de0d.fcac1","name":"","method":"POST","ret":"txt","url":"","tls":"","x":521,"y":269,"wires":[["395a11ef.5f572e"]]}]
把節點匯入
Function Node是根據API.AI文件用POST下請求一定要的參數,最後包在msh物件裡面回傳並輸出
http request記得用POST
最後什麼都不用做,拉一個debug節點就可以取得回應了,比起用java來做省力很多


Multiple Messages的展示:以官網的範例展示
就是如果輸出有多個訊息,會依序從第一個開始輸出至最後一個
依順序,第一個先拉Inject、兩個function、兩個debug,且第二個function串兩個debug
  1. [{"id":"480852e1.561e6c","type":"function","z":"eb44de0d.fcac1","name":"","func":"\nvar msg1 = { payload:\"first out of output 1\" };\nvar msg2 = { payload:\"second out of output 1\" };\nvar msg3 = { payload:\"third out of output 1\" };\nvar msg4 = { payload:\"only message from output 2\" };\nreturn [ [ msg1, msg2, msg3 ], msg4 ];","outputs":"1","noerr":0,"x":352,"y":274,"wires":[["383205a4.13ad7a"]]},{"id":"395a11ef.5f572e","type":"debug","z":"eb44de0d.fcac1","name":"","active":true,"console":"false","complete":"payload","x":708,"y":238,"wires":[]},{"id":"faef2961.d55798","type":"debug","z":"eb44de0d.fcac1","name":"","active":true,"console":"false","complete":"payload","x":705,"y":310,"wires":[]},{"id":"383205a4.13ad7a","type":"function","z":"eb44de0d.fcac1","name":"","func":"var outputMsgs = [];\nvar words = msg.payload.split(\" \");\nfor (var w in words) {\n outputMsgs.push({payload:words[w]});\n}\nreturn [ outputMsgs ];","outputs":"2","noerr":0,"x":515,"y":279,"wires":[["395a11ef.5f572e"],["faef2961.d55798"]]},{"id":"ea46f50f.753b08","type":"inject","z":"eb44de0d.fcac1","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":186,"y":274,"wires":[["480852e1.561e6c"]]}]
結果會是這樣

其他一些Function節點提供的功能:

  • 非同步傳送訊息(Sending messages asynchronously)
  • Log事件(Logging events)
  • 錯誤處理(Handling errors)
  • 儲存資料(Storing data):可以在上下文儲存資料
  • Flow context:官方是說這是整個Flow等級的Context,可以讓所有節點共享,而不是只有function節點
  • 全域的Context(Global context):讓所有節點都可以存取變數的功能?
  • 在節點旁附上狀態(Adding Status)

創造自定義的節點 - Creating Nodes


Node-RED除了提供內建的節點,還可以自行定義創造屬於自己獨一無二的節點

一個節點由兩個檔案組成,分別是Javascript和html檔
就來用官方範例來創創看,是一個把內容轉變為小寫的例子
創建一個目錄,裡面有以下的檔案:
package.json
lower-case.js
lower-case.html

1.先建構一個標準的package.json
打入此指令
$ npm init
會跳出一段訊息,然後會要你輸入一些資料,就是這個描述檔的內容
package name打入node-red-contrib-example-lower-case
description打入內容轉成小寫
其他enter跳過就可以
最後他會產生在家目錄底下,叫package.json
  1. {
  2. "name": "node-red-contrib-example-lower-case",
  3. "node-red": {
  4. "nodes": {
  5. "lower-case": "lower-case.js"
  6. }
  7. },
  8. "version": "1.0.0",
  9. "description": "內容轉成小寫",
  10. "main": "index.js",
  11. "scripts": {
  12. "test": "echo \"Error: no test specified\" && exit 1"
  13. },
  14. "author": "",
  15. "license": "ISC"
  16. }
  17.  
並在裡面加入node-red的片段,如上
另外文件有一頁是針對打包所寫的有需要可以看看 http://bit.ly/2tPat67

lower-case.js檔的部分
  1. module.exports = function(RED) {
  2. function LowerCaseNode(config) {
  3. RED.nodes.createNode(this,config);
  4. var node = this;
  5. node.on('input', function(msg) {
  6. msg.payload = msg.payload.toLowerCase();
  7. node.send(msg);
  8. });
  9. }
  10. RED.nodes.registerType("lower-case",LowerCaseNode);
  11. }

lower-case.html檔
  1. <script type="text/javascript">
  2. RED.nodes.registerType('lower-case', {
  3. category: 'function',
  4. color: '#a6bbcf',
  5. defaults: {
  6. name: {
  7. value: ""
  8. }
  9. },
  10. inputs: 1,
  11. outputs: 1,
  12. icon: "file.png",
  13. label: function() {
  14. return this.name || "lower-case";
  15. }
  16. });
  17. </script>
  18. <script type="text/x-red" data-template-name="lower-case">
  19. <div class="form-row">
  20. <label for="node-input-name"><i class="icon-tag"></i> Name</label>
  21. <input type="text" id="node-input-name" placeholder="Name">
  22. </div>
  23. </script>
  24. <script type="text/x-red" data-help-name="lower-case">
  25. <p>A simple node that converts the message payloads into all lower-case characters</p>
  26. </script>
這裡注意一下defaults物件,它裡面的東西是Node的屬性,name只是其中一個,所以還有其他很多屬性。
name屬性是在面板上這個Node會顯示的名稱。



接下來,把它安裝到Node-RED上
用終端機移動到package.json檔案的目錄,並下:
$ sudo npm link
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN node-red-contrib-example-lower-case@1.0.0 No repository field.

up to date in 0.099s
/Users/xanxus/.nvm/versions/node/v6.11.0/lib/node_modules/node-red-contrib-example-lower-case -> /Users/xanxus/lower-case
上面是他回應的訊息
重啟Node-RED,打開右邊的選單選Manage palette會發現node-red-contrib-example-lower-case
應該是我們剛剛建的...
回去面板,左邊列表找找看就有了
如果要更新的話,就再下一次指令就好,然後重啟Node-RED


接下來是.js檔案的細節部分:

Node的.js檔主要定義Node執行時的行為

節點由建構函數所構成,並在執行時註冊。
首先要先呼叫RED.nodes.createNode來初始化所有節點都有的特徵:
  1. function SampleNode(config) {
  2. RED.nodes.createNode(this,config);
  3. // node-specific code goes here
  4.  
  5. }
  6.  
  7. RED.nodes.registerType("sample",SampleNode);

並在監聽器上註冊input事件,以從Flow接收訊息
  1. this.on('input', function(msg) {
  2. // do something with 'msg'
  3. });

並使用send函數寄送訊息
  1. var msg = { payload:"hi" }
  2. this.send(msg);
如果msg為空,則不會發送任何訊息

另外還有close事件
如果節點要執行其他非同步工作,則註冊的監聽器需引入一個參數,該參數是在所有工作完成時才會被呼叫。

文件寫到,在命名節點時,最好符合官方的命名規則
例如,節點類型是sample-node,對外公開的名稱是colour,那在設定檔的名稱就要叫sampleNodeColour

如果要把設置公開給編輯器,那要像以下這樣寫:
  1. RED.nodes.registerType("sample",SampleNode, {
  2. settings: {
  3. sampleNodeColour: {
  4. value: "red",
  5. exportable: true
  6. }
  7. }
  8. });


再來是HTML檔的詳細:

HTML檔主要定義節點在面板上呈現的方式。
它包含三個不同的部分,每個部分都含有<script>的標籤。

第一部分:定義節點的一些外觀屬性,如顏色、icon、類型...
第二部分:定義節點的表單
第三部分:定義輔助的文字說明


第一部分:定義節點的一些外觀屬性,如顏色、icon、類型...
必須用一個叫RED.nodes.registerType的函數來定義,此函數有兩個參數,為node-type和該函數裡面的內容(你要做的事情.....)
  1. <script type="text/javascript">
  2. RED.nodes.registerType('node-type',{
  3. // node definition
  4. });
  5. </script> 
Note:其中node-type必須與.js檔匹配。如果這個節點叫lower-case,那js檔那裡也要叫lower-case。
Note:node-type是面板上會呈現的名稱。如果是in和out,那呈現的還是原本的名稱,如內建的mqtt in、mqtt out,分別放在input和output的地方,但info的Node Type是mqtt in。

定義它給的內建的屬性值
官方的範例有這些值category、color、defaults、inputs、outputs、icon........這只是一部分
其他可以定義的屬性:
category: (string) the palette category the node appears in
defaults: (object) the editable properties for the node.
credentials: (object) the credential properties for the node.
inputs: (number) how many inputs the node has, either 0 or 1.
outputs: (number) how many outputs the node has. Can be 0 or more.
color: (string) the background colour to use.
paletteLabel: (string|function) the label to use in the palette.
label: (string|function) the label to use in the workspace.
labelStyle: (string|function) the style to apply to the label.
inputLabels: (string|function) optional label to add on hover to the input port of a node.
outputLabels: (string|function) optional labels to add on hover to the output ports of a node.
icon: (string) the icon to use.
align: (string) the alignment of the icon and label.
oneditprepare: (function) called when the edit dialog is being built. See custom edit behaviour.
oneditsave: (function) called when the edit dialog is okayed. See custom edit behaviour.
oneditcancel: (function) called when the edit dialog is cancelled. See custom edit behaviour.
oneditdelete: (function) called when the delete button in a configuration node’s edit dialog is pressed. See custom edit behaviour.
oneditresize: (function) called when the edit dialog is resized. See custom edit behaviour.
onpaletteadd: (function) called when the node type is added to the palette.
onpaletteremove: (function) called when the node type is removed from the palette.


第二部:定義節點的表單
它定義的是這個地方的介面:
官方文件裡叫做dialog(對話框)




第三部分沒什麼,不寫

Node context [11]

節點可以利用context物件儲存資料,每次重新部署、重新啟動Node-RED Context都會被重置。

Node properties

defaults物件是定義在.html裡面
這裡範例主要是示範如何在.js程式碼中用.html定義的屬性值
做完之後的結果是這樣:


Property definitions [12]

屬性裡面可以再定義屬性...

defaults: {
     name: {value:""},
     prefix: {value:""}
 },
除了value還有required(布林型態)、validate(function)、type

Node credentials [13]

有時我們有一些私密的屬性值,如帳號密碼等....不希望被匯出,可以把這些定義在這個地方這樣就不會被匯出的樣子?

官方文件上也有寫如何存取在這定義的屬性值

Node appearance [14]

主要是定義Node的外觀

Node status [15]

定義Node目前執行的狀態,狀態會顯示在這個Node底下。

Node help style guide [16]

這裡是教你寫右邊info裡面的輔助資訊,就是下面那個地方:



Raspberry Pi上的NodeRED [17]

安裝完Pi的作業系統,會預設幫你裝好Node.js和NodeRED,但版本是蠻舊的,接下來我們要把這兩個升級版本:
$ update-nodejs-and-nodered
過程中會把他要做的事情列一個清單,做完就打勾
更新完後,啟動NodeRED,會發現版本都更新了
Node-RED version: v0.17.5
Node.js  version: v6.11.2
因為裝新的Node.js所以npm剛剛也裝好了,原本是沒有npm
$ npm --version
3.10.10
Note:npm最新版本是4,但官方不推薦使用

更新python-gpio
$ sudo apt-get update && sudo apt-get install python-rpi.gpio

NodeRED啟動的方式在Pi上有分兩種:
第一種是文件比較早提到的
$ sudo systemctl enable nodered.service
這個指令,在Pi開機的時候啟動NodeRED

第二種是SystemD,也是官方推薦的方式
他是把啟動和停止和開機就啟動寫成一個腳本,讓你可用一個指令來操作
很像已經幫你裝好了,我直接下$ node-red-start 就啟動了
停止的話還是要下$ node-red-stop 來停止

問題一:遇到找不到package.json的錯誤訊息
http://bit.ly/2xVESUn

問題二:一開啟NodeRED,上次拉的Block都不見了
有次原本辛辛苦苦拉的方塊,也部署完了,隔一陣子沒用NodeRED,再次開啟NodeRED時拉的Node(or Block)都不見了,然後Console就會出現一些訊息,說什麼Flow不存在...
至今原因未知,但有找到解決方法,經調查後並不是不見,而存在其他Flow檔,去.node-red底下發現有4個Flow的json檔,為:
flows_XXXXXMacde-MBP.json
flows_XXXXXMacde-MBP_cred.json
flows_XXXXXMacde-MacBook-Pro.local.json
flows_XXXXXMacde-MacBook-Pro.local_cred.json

首先我們一打開NodeRED,拉的Node或是說Block,他們都存放在flow裡面,當然我們可能會建立很多Flow來放Node,但她最後都會存放在同一個檔案裡面,就是像上面flows_XXXXXMacde-MBP.json或是flows_XXXXXMacde-MacBook-Pro.local.json
XXXXX就是我這台電腦的名稱
那不知為何有時打開它就沒有抓到原本的flow.json檔,而是抓到另一個,也就是說原本拉的還在!但有時他又會自己抓回原本的。

所以,請打開~/.node-red/settings.js檔案,找到flowFile這個屬性,它原本是被註解掉的,去除註解並打上你NodeRED要抓的那個Flow.json檔,像這樣:
flowFile: 'flows_XXXXXMacde-MacBook-Pro.local.json'
這樣他就會固定讀這個Flow檔了。
重啟NodeRED,成功。

Context

Context就是可以在Node與Node之間或Flow與Flow之間儲存資料。
有分三種:
  1. 一般的Context
  2. Flow等級的Context
  3. Global等級的Context
以下用一些簡單的範例來測試這幾種Context的特性。

下圖是一般的Context:
Context 1與Context 2的原始碼都一樣,如下:
  1. // initialise the counter to 0 if it doesn't exist already
  2. var count = context.get('count')||0;
  3. count += 1;
  4. // store the value back
  5. context.set('count',count);
  6. // make it part of the outgoing msg object
  7. msg.count = count;
  8. return msg;
程式很簡單,就是數字累加而已。
而這種一般的Context只會儲存同一個Flow的值,也就是Context 1與Context 2都是獨立的,Context 1不會儲存到Context 2的值,即使定義的變數名稱一樣也是互相獨立。
上圖是觸發Context 1兩次和觸發Context 2一次的結果,印證了上述的說明。
每次重新部署和NodeRED重新啟動後,都會重置儲存的Context值。
並且也只作用於同一個Flow頁籤。

下圖是Flow等級的Context:

Flow Context 1和Flow Context 2原始碼都一樣,如下:
  1. // initialise the counter to 0 if it doesn't exist already
  2. //var count = context.get('count')||0;
  3. var count = flow.get('count')||0;
  4. count += 1;
  5. // store the value back
  6. flow.set('count',count);
  7. // make it part of the outgoing msg object
  8. msg.count = count;
  9. return msg;
  10.  
下圖是對Flow Context 1觸發兩次,Flow Context 2觸發一次的實驗:
可以發現他們共用了變數的資料。
下圖為重新部署的實驗結果:
發現他們會紀錄上次的值,也就是說重新部署不會洗掉。
但是NodeRED重新啟動後就會洗掉重置了。
並且它只儲存同一個Flow頁籤,也就是說再開一個新的Flow頁籤並不會持續記錄,就會是一個新的獨立的個體物件。

下圖是Global等級的Context,位於第一個Flow頁面:
下圖位於第二個Flow頁面:
下圖是分別觸發Global Context 1到Global Context 3的結果:
結論是,Global等級的Context可以在不同的Flow頁面之間儲存資料。
重新部署不會洗掉。
但NodeRED重新啟動就會洗掉。


Context層級\特性作用範圍不同條的Flow是否共用不同Flow頁籤是否共用重新部署是否會保留物件重啟NodeRED是否會保留物件
Node只存在於設置這個Node層級變數的這個節點中
Flow可存在於多條Flow、多個節點中
Global可存在於多條Flow且不同頁籤的Flow中

參考資料:

  1. Running https://nodered.org/docs/getting-started/running
  2. Upgrading https://nodered.org/docs/getting-started/upgrading
  3. Adding Nodes https://nodered.org/docs/getting-started/adding-nodes
  4. Creating your second flow https://nodered.org/docs/getting-started/second-flow
  5. Configuration https://nodered.org/docs/configuration
  6. Security https://nodered.org/docs/security
  7. Command-line Administration https://nodered.org/docs/node-red-admin
  8. Writing Functions https://nodered.org/docs/writing-functions
  9. Creating Nodes https://nodered.org/docs/creating-nodes/
  10. Packaging https://nodered.org/docs/creating-nodes/packaging
  11. Node context https://nodered.org/docs/creating-nodes/context
  12. Property definitions https://nodered.org/docs/creating-nodes/properties
  13. Node credentials https://nodered.org/docs/creating-nodes/credentials
  14. Node appearance https://nodered.org/docs/creating-nodes/appearance
  15. Node status https://nodered.org/docs/creating-nodes/status
  16. Node help style guide https://nodered.org/docs/creating-nodes/help-style-guide
  17. Running on Raspberry Pi https://nodered.org/docs/hardware/raspberrypi.html
  18. 週二來一招, Node-RED 簡單講講 (Node-RED)http://bit.ly/2ey5FLu

沒有留言:

張貼留言

WOKWI DHT22 & LED , Node-Red + SQLite database

 WOKWI DHT22 & LED , Node-Red + SQLite database Node-Red程式 [{"id":"6f0240353e534bbd","type":"comment&...