@operato/scene-mqtt 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/README_KR.md +119 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/mqtt.d.ts +55 -0
- package/dist/mqtt.js +198 -0
- package/dist/mqtt.js.map +1 -0
- package/helps/scene/component/mqtt.ko.md +110 -0
- package/helps/scene/component/mqtt.md +110 -0
- package/helps/scene/component/mqtt.zh.md +108 -0
- package/icons/mqtt.png +0 -0
- package/package.json +61 -0
- package/src/index.ts +4 -0
- package/src/mqtt.ts +245 -0
- package/test/basic-test.html +67 -0
- package/test/index.html +22 -0
- package/test/unit/test-mqtt.js +33 -0
- package/test/unit/util.js +22 -0
- package/things-scene.config.js +24 -0
- package/tsconfig.json +22 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# MQTT
|
|
2
|
+
|
|
3
|
+
MQTT broker에 연결되어, 설정된 topic 데이터를 publish/subscribe하는 데이타소스 컴포넌트이다.
|
|
4
|
+
|
|
5
|
+
- [MQTT 웹소켓 프로토콜](http://www.steves-internet-guide.com/mqtt-websockets/)를 사용한다.
|
|
6
|
+
- publish
|
|
7
|
+
- 이 컴포넌트의 value를 변경하면, 설정된 topic으로 value를 publish한다.
|
|
8
|
+
- subscribe
|
|
9
|
+
- 설정된 topic으로 subscribe되는 데이타를 서브스크라이브해서 data를 변경한다.
|
|
10
|
+
- data가 변경되면 데이타바인딩이 작동한다.
|
|
11
|
+
|
|
12
|
+
## properties
|
|
13
|
+
|
|
14
|
+
### MQTT 브로커로 mosquitto를 사용한 경우
|
|
15
|
+
|
|
16
|
+
- broker : hostname of the broker
|
|
17
|
+
- port : websocket service port number (default 1884)
|
|
18
|
+
- path : '/mqtt'
|
|
19
|
+
- user : username or blank
|
|
20
|
+
- password : password or blank
|
|
21
|
+
- topic : topic
|
|
22
|
+
- qos : QOS level [0, 1, 2]
|
|
23
|
+
- client-id : (unique) client id
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
클라이언트 아이디는 (브로커 입장에서) 유일한 연결 노드의 이름을 의미하며, 브로커에서 모니터링하기 위한 용도로 유일하게 적어준다.
|
|
27
|
+
클라이언트 아이디 속성을 비워두면, 자동으로 'THINGS-BOARD-{role}-{timestamp}‘로 자동 생성된다.
|
|
28
|
+
클라이언트 아이디 속성을 입력하면, 자동으로 '{client-id}-{role}-{timestamp}'로 자동 생성된다.
|
|
29
|
+
클라이언트 아이디 속성에 timestamp를 추가하는 이유는, 유일한 아이디를 만들기 위해서이다.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- data-format : [Plain Text, JSON]
|
|
33
|
+
- retain : true or false
|
|
34
|
+
- ssl : true or false (false)
|
|
35
|
+
|
|
36
|
+
### RabbitMQ의 MQTT-Websocket 플러그인을 사용한 경우
|
|
37
|
+
|
|
38
|
+
- broker : hostname of the broker
|
|
39
|
+
- port : websocket service port number (default 15675)
|
|
40
|
+
- path : '/ws'
|
|
41
|
+
- user : username or blank
|
|
42
|
+
- password : password or blank
|
|
43
|
+
- topic : topic
|
|
44
|
+
- qos : QOS level [0, 1, 2]
|
|
45
|
+
- client-id : (unique) client id
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
클라이언트 아이디는 (브로커 입장에서) 유일한 연결 노드의 이름을 의미하며, 브로커에서 모니터링하기 위한 용도로 유일하게 적어준다.
|
|
49
|
+
클라이언트 아이디 속성을 비워두면, 자동으로 'THINGS-BOARD-{role}-{timestamp}‘로 자동 생성된다.
|
|
50
|
+
클라이언트 아이디 속성을 입력하면, 자동으로 '{client-id}-{role}-{timestamp}'로 자동 생성된다.
|
|
51
|
+
클라이언트 아이디 속성에 timestamp를 추가하는 이유는, 유일한 아이디를 만들기 위해서이다.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- data-format : [Plain Text, JSON]
|
|
55
|
+
- retain : true or false
|
|
56
|
+
- ssl : true or false (false)
|
|
57
|
+
|
|
58
|
+
## Rabbit MQ의 MQTT-Websocket 플러그인을 사용하는 경우 메시지 Exchange
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
When using the MQTT-Websocket plugin of Rabbit MQ,
|
|
62
|
+
It is routed by a durable'topic' type of'amq.topic' exchange.
|
|
63
|
+
Therefore, the topic attribute of the MQTT Data Source above serves as a routing key.
|
|
64
|
+
|
|
65
|
+
To be able to receive from MQTT Data Source using AMQP of Rabbit MQ,
|
|
66
|
+
See the javascript sample code below.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
var amqp = require('amqplib/callback_api');
|
|
71
|
+
|
|
72
|
+
amqp.connect('amqp://hatiolab:hatiolab@mq.hatiolab.com', function(err, conn) {
|
|
73
|
+
if(err) {
|
|
74
|
+
console.error(err);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
conn.createChannel(function (err, ch) {
|
|
79
|
+
// Set the exchange to amq.topic and set the durable option to true.
|
|
80
|
+
var ex = 'amq.topic';
|
|
81
|
+
|
|
82
|
+
ch.assertExchange(ex, 'topic', { durable: true });
|
|
83
|
+
|
|
84
|
+
var location = {
|
|
85
|
+
x: 100,
|
|
86
|
+
y: 200
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// When the topic property is set to location.
|
|
90
|
+
ch.publish(ex, 'location', new Buffer(JSON.stringify(location)));
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Creating an MQTT environment with mosquitto (based on macOS)
|
|
96
|
+
|
|
97
|
+
### MQTT 브로커로 mosquitto 설치하기
|
|
98
|
+
|
|
99
|
+
- homebrew를 이용해서 mosquitto를 설치한다.
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
$ brew install mosquitto
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- Things Scene은 브라우저용이므로, MQTT 브로커에 웹소켓으로 접속할 수 있어야 한다. 그래서, mosquitto에 웹서비스 기능을 활성화한다.
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
$ echo -e "listener 1884\nprotocol websockets\nlistener 1883\nprotocol mqtt" >> /usr/local/opt/mosquitto/etc/mosquitto/mosquitto.conf
|
|
109
|
+
$ brew services restart mosquitto
|
|
110
|
+
```
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# MQTT
|
|
2
|
+
|
|
3
|
+
It is a data source component that connects to the MQTT broker and publishes/subscribes to the topic data set.
|
|
4
|
+
|
|
5
|
+
- Use [MQTT Websockets Protocol](http://www.steves-internet-guide.com/mqtt-websockets/).
|
|
6
|
+
- Publish
|
|
7
|
+
-When the value of this component is changed, the value will be published to the set topic.
|
|
8
|
+
- Subscribe
|
|
9
|
+
-Subscribe to subscribed topic data and change the data.
|
|
10
|
+
-When data is changed, data binding is valid.
|
|
11
|
+
|
|
12
|
+
## properties
|
|
13
|
+
|
|
14
|
+
### When using mosquitto as MQTT broker
|
|
15
|
+
|
|
16
|
+
- broker : hostname of the broker
|
|
17
|
+
- port : websocket service port number (default 1884)
|
|
18
|
+
- path : '/mqtt'
|
|
19
|
+
- user : username or blank
|
|
20
|
+
- password : password or blank
|
|
21
|
+
- topic : topic
|
|
22
|
+
- qos : QOS level [0, 1, 2]
|
|
23
|
+
- client-id : (unique) client id
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
The client ID refers to the name of the only connected node (from the broker's point of view), and it is uniquely written for monitoring in the broker.
|
|
27
|
+
If the client ID attribute is left blank, it is automatically created as'THINGS-BOARD-{role}-{timestamp}'.
|
|
28
|
+
When the client ID attribute is entered, it is automatically created as'{client-id}-{role}-{timestamp}'.
|
|
29
|
+
The reason for adding timestamp to the client ID attribute is to create a unique ID.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- data-format : [Plain Text, JSON]
|
|
33
|
+
- retain : true or false
|
|
34
|
+
- ssl : true or false (false)
|
|
35
|
+
|
|
36
|
+
### When using RabbitMQ's MQTT-Websocket plugin
|
|
37
|
+
|
|
38
|
+
- broker : hostname of the broker
|
|
39
|
+
- port : websocket service port number (default 15675)
|
|
40
|
+
- path : '/ws'
|
|
41
|
+
- user : username or blank
|
|
42
|
+
- password : password or blank
|
|
43
|
+
- topic : topic
|
|
44
|
+
- qos : QOS level [0, 1, 2]
|
|
45
|
+
- client-id : (unique) client id
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
The client ID refers to the name of the only connected node (from the broker's point of view), and it is uniquely written for monitoring in the broker.
|
|
49
|
+
If the client ID attribute is left blank, it is automatically created as'THINGS-BOARD-{role}-{timestamp}'.
|
|
50
|
+
When the client ID attribute is entered, it is automatically created as'{client-id}-{role}-{timestamp}'.
|
|
51
|
+
The reason for adding timestamp to the client ID attribute is to create a unique ID.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- data-format : [Plain Text, JSON]
|
|
55
|
+
- retain : true or false
|
|
56
|
+
- ssl : true or false (false)
|
|
57
|
+
|
|
58
|
+
### Message exchange when using Rabbit MQ's MQTT-Websocket plug-in
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
使用Rabbit MQ的MQTT-Websocket插件时,
|
|
62
|
+
由durable 'topic'类型的“ amq.topic”的exchange来路由。
|
|
63
|
+
因此,上述MQTT数据源的主题属性用作路由键。
|
|
64
|
+
|
|
65
|
+
为了从Rabbit MQ的AMQP获取MQTT Data Source,
|
|
66
|
+
请参见下面的javascript示例代码。
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
var amqp = require('amqplib/callback_api');
|
|
71
|
+
|
|
72
|
+
amqp.connect('amqp://hatiolab:hatiolab@mq.hatiolab.com', function(err, conn) {
|
|
73
|
+
if(err) {
|
|
74
|
+
console.error(err);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
conn.createChannel(function (err, ch) {
|
|
79
|
+
// Set the exchange to amq.topic and set the durable option to true.
|
|
80
|
+
var ex = 'amq.topic';
|
|
81
|
+
|
|
82
|
+
ch.assertExchange(ex, 'topic', { durable: true });
|
|
83
|
+
|
|
84
|
+
var location = {
|
|
85
|
+
x: 100,
|
|
86
|
+
y: 200
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// When the topic property is set to location.
|
|
90
|
+
ch.publish(ex, 'location', new Buffer(JSON.stringify(location)));
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Creating an MQTT environment with mosquitto (based on macOS)
|
|
96
|
+
|
|
97
|
+
### Install mosquitto as MQTT broker
|
|
98
|
+
|
|
99
|
+
- Use homebrew to install mosquitto.
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
$ brew install mosquitto
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- Things Scene is executed in the browser, so it needs to connect to the MQTT proxy via Web Socket. Therefore, you need to enable the Web service function in mosquitto.
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
$ echo -e "listener 1884\nprotocol websockets\nlistener 1883\nprotocol mqtt" >> /usr/local/opt/mosquitto/etc/mosquitto/mosquitto.conf
|
|
109
|
+
$ brew services restart mosquitto
|
|
110
|
+
```
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# MQTT
|
|
2
|
+
|
|
3
|
+
是连接到MQTT代理并发布/订阅设置的主题数据的数据源组件。
|
|
4
|
+
|
|
5
|
+
- 使用 [MQTT Websockets协议](http://www.steves-internet-guide.com/mqtt-websockets/)。
|
|
6
|
+
- 发布
|
|
7
|
+
- 更改此组件的值时,该值将发布到设置的主题。
|
|
8
|
+
- 订阅
|
|
9
|
+
- 订阅预订的主题数据并更改数据。
|
|
10
|
+
- 更改数据时,数据绑定有效。
|
|
11
|
+
|
|
12
|
+
## properties
|
|
13
|
+
|
|
14
|
+
### 使用mosquitto作为MQTT代理时
|
|
15
|
+
|
|
16
|
+
- 代理 : hostname of the broker
|
|
17
|
+
- 端口 : websocket service port number (default 1884)
|
|
18
|
+
- 路径 : '/mqtt'
|
|
19
|
+
- 用户 : username or blank
|
|
20
|
+
- 密码 : password or blank
|
|
21
|
+
- 主题 : topic
|
|
22
|
+
- qos : QOS level [0, 1, 2]
|
|
23
|
+
- 客户端ID : (unique) client id
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
从代理的角度来看,客户端ID是指唯一连接的节点的名称。
|
|
27
|
+
如果客户端ID属性留空,则会自动将其创建为“ THINGS-BOARD- {role}-{timestamp}”。
|
|
28
|
+
输入客户ID属性后,系统会自动将其创建为“ {client-id}-{role}-{timestamp}”。
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- 数据格式 : [Plain Text, JSON]
|
|
32
|
+
- 保持 : true or false
|
|
33
|
+
- ssl : true or false (false)
|
|
34
|
+
|
|
35
|
+
### 使用RabbitMQ的MQTT-Websocket插件时
|
|
36
|
+
|
|
37
|
+
- 代理 : hostname of the broker
|
|
38
|
+
- 端口 : websocket service port number (default 15675)
|
|
39
|
+
- 路径 : '/ws'
|
|
40
|
+
- 用户 : username or blank
|
|
41
|
+
- 密码 : password or blank
|
|
42
|
+
- 主题 : topic
|
|
43
|
+
- qos : QOS level [0, 1, 2]
|
|
44
|
+
- 客户端ID : (unique) client id
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
从代理的角度来看,客户端ID是指唯一连接的节点的名称。
|
|
48
|
+
如果客户端ID属性留空,则会自动将其创建为“ THINGS-BOARD- {role}-{timestamp}”。
|
|
49
|
+
输入客户ID属性后,系统会自动将其创建为“ {client-id}-{role}-{timestamp}”。
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- 数据格式 : [Plain Text, JSON]
|
|
53
|
+
- 保持 : true or false
|
|
54
|
+
- ssl : true or false (false)
|
|
55
|
+
|
|
56
|
+
### 使用Rabbit MQ的MQTT-Websocket插件时的消息交换
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
使用Rabbit MQ的MQTT-Websocket插件时,
|
|
60
|
+
由durable 'topic'类型的“ amq.topic”的exchange来路由。
|
|
61
|
+
因此,上述MQTT数据源的主题属性用作路由键。
|
|
62
|
+
|
|
63
|
+
为了从Rabbit MQ的AMQP获取MQTT Data Source,
|
|
64
|
+
请参见下面的javascript示例代码。
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
var amqp = require('amqplib/callback_api');
|
|
69
|
+
|
|
70
|
+
amqp.connect('amqp://hatiolab:hatiolab@mq.hatiolab.com', function(err, conn) {
|
|
71
|
+
if(err) {
|
|
72
|
+
console.error(err);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
conn.createChannel(function (err, ch) {
|
|
77
|
+
// exchange配置为amq.topic, durable option为true.
|
|
78
|
+
var ex = 'amq.topic';
|
|
79
|
+
|
|
80
|
+
ch.assertExchange(ex, 'topic', { durable: true });
|
|
81
|
+
|
|
82
|
+
var location = {
|
|
83
|
+
x: 100,
|
|
84
|
+
y: 200
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// topic属性为location时
|
|
88
|
+
ch.publish(ex, 'location', new Buffer(JSON.stringify(location)));
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 使用mosquitto创建MQTT环境(基于macOS)
|
|
94
|
+
|
|
95
|
+
### 将mosquitto安装为MQTT代理
|
|
96
|
+
|
|
97
|
+
- 使用homebrew安装mosquitto。
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
$ brew install mosquitto
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- Things Scene执行于浏览器,因此需要能够通过Web Socket连接到MQTT代理。 因此,需要在mosquitto中启用Web服务功能。
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
$ echo -e "listener 1884\nprotocol websockets\nlistener 1883\nprotocol mqtt" >> /usr/local/opt/mosquitto/etc/mosquitto/mosquitto.conf
|
|
107
|
+
$ brew services restart mosquitto
|
|
108
|
+
```
|
package/icons/mqtt.png
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@operato/scene-mqtt",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "MQTT integration component for things-scene",
|
|
5
|
+
"things-scene": true,
|
|
6
|
+
"browser": "src/index.js",
|
|
7
|
+
"author": "heartyoh",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public",
|
|
11
|
+
"@operato:registry": "https://registry.npmjs.org"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/things-scene/operato-scene.git",
|
|
16
|
+
"directory": "packages/mqtt"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"serve": "tsc && things-factory-dev",
|
|
20
|
+
"start": "tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds\"",
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepublish": "tsc",
|
|
23
|
+
"lint": "eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore",
|
|
24
|
+
"format": "eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore",
|
|
25
|
+
"migration": "things-factory-migration"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@hatiolab/things-scene": "^2.7.33",
|
|
29
|
+
"mqtt": "^4.3.4"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@hatiolab/prettier-config": "^1.0.0",
|
|
33
|
+
"@operato/board": "^0.3.20",
|
|
34
|
+
"@things-factory/builder": "^4.0.38",
|
|
35
|
+
"@things-factory/operato-board": "^4.0.38",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
|
37
|
+
"@typescript-eslint/parser": "^4.33.0",
|
|
38
|
+
"@web/dev-server": "^0.1.28",
|
|
39
|
+
"concurrently": "^5.3.0",
|
|
40
|
+
"eslint": "^7.32.0",
|
|
41
|
+
"eslint-config-prettier": "^8.3.0",
|
|
42
|
+
"husky": "^4.3.8",
|
|
43
|
+
"lint-staged": "^10.5.4",
|
|
44
|
+
"prettier": "^2.4.1",
|
|
45
|
+
"tslib": "^2.3.1",
|
|
46
|
+
"typescript": "^4.5.2"
|
|
47
|
+
},
|
|
48
|
+
"prettier": "@hatiolab/prettier-config",
|
|
49
|
+
"husky": {
|
|
50
|
+
"hooks": {
|
|
51
|
+
"pre-commit": "lint-staged"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"lint-staged": {
|
|
55
|
+
"*.ts": [
|
|
56
|
+
"eslint --fix",
|
|
57
|
+
"prettier --write"
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
"gitHead": "f3275af95afaf8389224e1e507df6155cbf39382"
|
|
61
|
+
}
|
package/src/index.ts
ADDED
package/src/mqtt.ts
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import mqtt from 'mqtt'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright © HatioLab Inc. All rights reserved.
|
|
5
|
+
*/
|
|
6
|
+
import { Component, DataSource, Properties, RectPath, Shape } from '@hatiolab/things-scene'
|
|
7
|
+
|
|
8
|
+
const NATURE = {
|
|
9
|
+
mutable: false,
|
|
10
|
+
resizable: true,
|
|
11
|
+
rotatable: true,
|
|
12
|
+
properties: [
|
|
13
|
+
{
|
|
14
|
+
type: 'string',
|
|
15
|
+
label: 'broker',
|
|
16
|
+
name: 'broker',
|
|
17
|
+
placeholder: 'WebSocket hostname'
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'number',
|
|
21
|
+
label: 'port',
|
|
22
|
+
name: 'port',
|
|
23
|
+
placeholder: '15675'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'string',
|
|
27
|
+
label: 'path',
|
|
28
|
+
name: 'path',
|
|
29
|
+
placeholder: '/mqtt or /ws'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'string',
|
|
33
|
+
label: 'user',
|
|
34
|
+
name: 'user'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'string',
|
|
38
|
+
label: 'password',
|
|
39
|
+
name: 'password',
|
|
40
|
+
property: 'password'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'string',
|
|
44
|
+
label: 'topic',
|
|
45
|
+
name: 'topic'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: 'number',
|
|
49
|
+
label: 'qos',
|
|
50
|
+
name: 'qos',
|
|
51
|
+
placeholder: '0..2'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: 'string',
|
|
55
|
+
label: 'client-id',
|
|
56
|
+
name: 'clientId'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: 'select',
|
|
60
|
+
label: 'data-format',
|
|
61
|
+
name: 'dataFormat',
|
|
62
|
+
property: {
|
|
63
|
+
options: [
|
|
64
|
+
{
|
|
65
|
+
display: 'Plain Text',
|
|
66
|
+
value: 'text'
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
display: 'JSON',
|
|
70
|
+
value: 'json'
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
type: 'checkbox',
|
|
77
|
+
label: 'retain',
|
|
78
|
+
name: 'retain'
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'checkbox',
|
|
82
|
+
label: 'ssl',
|
|
83
|
+
name: 'ssl'
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
'value-property': 'message',
|
|
87
|
+
help: 'scene/component/mqtt'
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const MQTT_IMAGE =
|
|
91
|
+
'data:image/png;base64,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'
|
|
92
|
+
|
|
93
|
+
export default class Mqtt extends DataSource(RectPath(Shape)) {
|
|
94
|
+
private static _image: HTMLImageElement
|
|
95
|
+
|
|
96
|
+
private _client?: mqtt.MqttClient
|
|
97
|
+
|
|
98
|
+
static get image() {
|
|
99
|
+
if (!Mqtt._image) {
|
|
100
|
+
Mqtt._image = new Image()
|
|
101
|
+
Mqtt._image.src = MQTT_IMAGE
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Mqtt._image
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
ready() {
|
|
108
|
+
super.ready()
|
|
109
|
+
|
|
110
|
+
if (!this.app.isViewMode) return
|
|
111
|
+
|
|
112
|
+
this._initMqttConnection()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
_initMqttConnection() {
|
|
116
|
+
try {
|
|
117
|
+
this._client && this._client.end(true, () => {})
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.error(e)
|
|
120
|
+
}
|
|
121
|
+
delete this._client
|
|
122
|
+
|
|
123
|
+
var {
|
|
124
|
+
broker,
|
|
125
|
+
port = 8441,
|
|
126
|
+
clientId = 'THINGS-FACTORY',
|
|
127
|
+
topic,
|
|
128
|
+
qos = 1,
|
|
129
|
+
retain = false,
|
|
130
|
+
path = '/mqtt',
|
|
131
|
+
dataFormat = 'text',
|
|
132
|
+
user,
|
|
133
|
+
password,
|
|
134
|
+
ssl = false
|
|
135
|
+
} = this.model
|
|
136
|
+
|
|
137
|
+
if (!broker) {
|
|
138
|
+
console.warn('broker not defined')
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
clientId = [clientId, Date.now()].join('-')
|
|
143
|
+
|
|
144
|
+
var client = mqtt.connect(`ws://${broker}:${port}${path}`, {
|
|
145
|
+
keepalive: 10,
|
|
146
|
+
clientId,
|
|
147
|
+
protocolId: 'MQTT',
|
|
148
|
+
protocolVersion: 4,
|
|
149
|
+
clean: true,
|
|
150
|
+
reconnectPeriod: 1000,
|
|
151
|
+
connectTimeout: 30 * 1000,
|
|
152
|
+
will: {
|
|
153
|
+
topic: 'WillMsg',
|
|
154
|
+
payload: 'Connection Closed abnormally..!',
|
|
155
|
+
qos: 0,
|
|
156
|
+
retain: false
|
|
157
|
+
},
|
|
158
|
+
username: user,
|
|
159
|
+
password: password,
|
|
160
|
+
rejectUnauthorized: false
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
client.on('error', err => {
|
|
164
|
+
console.error(err)
|
|
165
|
+
client.end()
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
client.on('connect', packet => {
|
|
169
|
+
console.log('client connected:', clientId, packet)
|
|
170
|
+
|
|
171
|
+
client.subscribe(
|
|
172
|
+
topic,
|
|
173
|
+
{
|
|
174
|
+
qos
|
|
175
|
+
},
|
|
176
|
+
(err, granted) => {
|
|
177
|
+
if (!err) {
|
|
178
|
+
console.error('subscription failed', err)
|
|
179
|
+
} else {
|
|
180
|
+
console.log('mqtt subscription success for topic - ', topic)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
client.on('message', (topic, message, packet) => {
|
|
187
|
+
this.data = this._convertDataFormat(message.toString(), dataFormat)
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
client.on('close', () => {
|
|
191
|
+
console.log(clientId + ' disconnected')
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
this._client = client
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
dispose() {
|
|
198
|
+
try {
|
|
199
|
+
this._client && this._client.end(true, () => {})
|
|
200
|
+
} catch (e) {
|
|
201
|
+
console.error(e)
|
|
202
|
+
}
|
|
203
|
+
delete this._client
|
|
204
|
+
|
|
205
|
+
super.dispose()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_draw(context: CanvasRenderingContext2D): void {
|
|
209
|
+
var { left, top, width, height } = this.bounds
|
|
210
|
+
|
|
211
|
+
context.beginPath()
|
|
212
|
+
this.drawImage(context, Mqtt.image, left, top, width, height)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
get message() {
|
|
216
|
+
return this.getState('message')
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
set message(message) {
|
|
220
|
+
this.setState('message', message)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
onchange(after: Properties, before: Properties): void {
|
|
224
|
+
if ('message' in after) {
|
|
225
|
+
const { message } = after
|
|
226
|
+
|
|
227
|
+
if (!this._client || !this._client.connected) {
|
|
228
|
+
return
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
var { topic } = this.state
|
|
232
|
+
|
|
233
|
+
this._client.publish(topic, JSON.stringify(message), {
|
|
234
|
+
qos: 0,
|
|
235
|
+
retain: false
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
get nature() {
|
|
241
|
+
return NATURE
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
Component.register('mqtt', Mqtt)
|