@ircam/comote-helpers 0.3.3 → 1.0.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/LICENSE +2 -2
- package/README.md +5 -156
- package/package.json +13 -29
- package/src/.editorbackup/network-infos.js~ +60 -0
- package/src/.editorbackup/wifi-infos.js~ +33 -0
- package/{network-infos.js → src/network-infos.js} +25 -16
- package/{qrcode.js → src/qrcode.js} +32 -30
- package/{server.js → src/server.js} +60 -47
- package/{wifi-infos.js → src/wifi-infos.js} +8 -12
- package/.github/workflows/main.yml +0 -45
package/LICENSE
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c)
|
|
1
|
+
Copyright (c) 2022-present IRCAM, Paris, France
|
|
2
2
|
All rights reserved.
|
|
3
3
|
|
|
4
4
|
Redistribution and use in source and binary forms, with or without
|
|
@@ -25,4 +25,4 @@ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
25
25
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
26
26
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
27
27
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
28
|
-
POSSIBILITY OF SUCH DAMAGE.
|
|
28
|
+
POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,160 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CoMote helpers
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
> and Android `CoMo.te` application, cf. [https://ismm-apps.ircam.fr/comote](https://ismm-apps.ircam.fr/comote).
|
|
3
|
+
Javascript and Max helpers for usage with the the CoMote app.
|
|
5
4
|
|
|
5
|
+
Also compatible with R-IoT device.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
CoMo.te is especially designed for the CoMo applications family that enables real-time interaction between gestures/movements and sounds. Nevertheless, the CoMo.te application can be used with any software that makes use of the OSC protocol.
|
|
7
|
+
Get network information, and generate QR code for CoMote configuration.
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
CoMo.te and the CoMo applications are software designed and developed by IRCAM in the Sound-Music-Movement-Interaction team (UMR STMS).
|
|
14
|
-
|
|
15
|
-
This repository provides utilities to help to generate the QRCode in the target application both for Max/MSP and Node.js.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
## Table of Contents
|
|
20
|
-
|
|
21
|
-
<!-- toc -->
|
|
22
|
-
|
|
23
|
-
- [Max/MSP](#maxmsp)
|
|
24
|
-
- [JS](#js)
|
|
25
|
-
* [Node.js](#nodejs)
|
|
26
|
-
* [API](#api)
|
|
27
|
-
- [License](#license)
|
|
28
|
-
|
|
29
|
-
<!-- tocstop -->
|
|
30
|
-
|
|
31
|
-
## Max/MSP
|
|
32
|
-
|
|
33
|
-
Download the Max abstraction (i.e. CoMo.te.zip) from the lastest release in the releases page: https://github.com/ircam-ismm/comote-helpers/releases
|
|
34
|
-
Unzip the package and copy the resulting directory in `~/Documents/Max 8/Packages`
|
|
35
|
-
|
|
36
|
-
For now, the package only provide abstraction `[comote.connect]` that allows you to generate a QRCode that can be
|
|
37
|
-
flashed within the CoMo.te application to configure the OSC stream.
|
|
38
|
-
|
|
39
|
-
You can find more informations in the Helper patch: `~/Document/Max 8/Packages/CoMo.te/extras/CoMo.te.maxpat`
|
|
40
|
-
|
|
41
|
-
This version requires Max 7.1 or higher.
|
|
42
|
-
|
|
43
|
-
## JS
|
|
44
|
-
|
|
45
|
-
### Node.js
|
|
46
|
-
|
|
47
|
-
```sh
|
|
48
|
-
npm install --save @ircam/comote-helpers
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### API
|
|
52
|
-
|
|
53
|
-
<!-- api -->
|
|
54
|
-
|
|
55
|
-
#### Classes
|
|
56
|
-
|
|
57
|
-
<dl>
|
|
58
|
-
<dt><a href="#Server">Server</a></dt>
|
|
59
|
-
<dd><p>Launch WebSocket and/or OSC server according to given <code>CoMoteConfig</code> object</p>
|
|
60
|
-
</dd>
|
|
61
|
-
</dl>
|
|
62
|
-
|
|
63
|
-
#### Functions
|
|
64
|
-
|
|
65
|
-
<dl>
|
|
66
|
-
<dt><a href="#rawLink">rawLink()</a></dt>
|
|
67
|
-
<dd><p>Return the link to be encoded in the QRCode accroding to given <code>CoMoteConfig</code></p>
|
|
68
|
-
</dd>
|
|
69
|
-
<dt><a href="#terminal">terminal(config)</a></dt>
|
|
70
|
-
<dd><p>Create a qrcode to be logged in terminal according to given `CoMoteConfig``</p>
|
|
71
|
-
</dd>
|
|
72
|
-
<dt><a href="#dataURL">dataURL(config)</a></dt>
|
|
73
|
-
<dd><p>Create a qrcode to be used as in Image source according to given `CoMoteConfig``</p>
|
|
74
|
-
</dd>
|
|
75
|
-
</dl>
|
|
76
|
-
|
|
77
|
-
#### Typedefs
|
|
78
|
-
|
|
79
|
-
<dl>
|
|
80
|
-
<dt><a href="#CoMoteConfig">CoMoteConfig</a> : <code>Object</code></dt>
|
|
81
|
-
<dd></dd>
|
|
82
|
-
<dt><a href="#CoMoteTarget">CoMoteTarget</a> : <code>Object</code></dt>
|
|
83
|
-
<dd></dd>
|
|
84
|
-
</dl>
|
|
85
|
-
|
|
86
|
-
<a name="rawLink"></a>
|
|
87
|
-
|
|
88
|
-
#### rawLink()
|
|
89
|
-
Return the link to be encoded in the QRCode accroding to given `CoMoteConfig`
|
|
90
|
-
|
|
91
|
-
**Kind**: global function
|
|
92
|
-
<a name="terminal"></a>
|
|
93
|
-
|
|
94
|
-
#### terminal(config)
|
|
95
|
-
Create a qrcode to be logged in terminal according to given `CoMoteConfig``
|
|
96
|
-
|
|
97
|
-
**Kind**: global function
|
|
98
|
-
|
|
99
|
-
| Param | Type |
|
|
100
|
-
| --- | --- |
|
|
101
|
-
| config | [<code>CoMoteConfig</code>](#CoMoteConfig) |
|
|
102
|
-
|
|
103
|
-
**Example**
|
|
104
|
-
```js
|
|
105
|
-
console(await CoMoteQRCode.terminal(config));
|
|
106
|
-
```
|
|
107
|
-
<a name="dataURL"></a>
|
|
108
|
-
|
|
109
|
-
#### dataURL(config)
|
|
110
|
-
Create a qrcode to be used as in Image source according to given `CoMoteConfig``
|
|
111
|
-
|
|
112
|
-
**Kind**: global function
|
|
113
|
-
|
|
114
|
-
| Param | Type |
|
|
115
|
-
| --- | --- |
|
|
116
|
-
| config | [<code>CoMoteConfig</code>](#CoMoteConfig) |
|
|
117
|
-
|
|
118
|
-
**Example**
|
|
119
|
-
```js
|
|
120
|
-
const qrCode = await CoMoteQRCode.dataURL(config));
|
|
121
|
-
|
|
122
|
-
<img src="${qrCode}" />
|
|
123
|
-
```
|
|
124
|
-
<a name="CoMoteConfig"></a>
|
|
125
|
-
|
|
126
|
-
#### CoMoteConfig : <code>Object</code>
|
|
127
|
-
**Kind**: global typedef
|
|
128
|
-
**Properties**
|
|
129
|
-
|
|
130
|
-
| Name | Type | Description |
|
|
131
|
-
| --- | --- | --- |
|
|
132
|
-
| id | <code>String</code> | id of the client CoMo.te |
|
|
133
|
-
| interval | <code>Number</code> | period in ms of the sensors for the client CoMo.te |
|
|
134
|
-
| osc | [<code>CoMoteTarget</code>](#CoMoteTarget) | OSC configuration |
|
|
135
|
-
| ws | [<code>CoMoteTarget</code>](#CoMoteTarget) | WebSocket configuration |
|
|
136
|
-
|
|
137
|
-
<a name="CoMoteTarget"></a>
|
|
138
|
-
|
|
139
|
-
#### CoMoteTarget : <code>Object</code>
|
|
140
|
-
**Kind**: global typedef
|
|
141
|
-
**Properties**
|
|
142
|
-
|
|
143
|
-
| Name | Type | Description |
|
|
144
|
-
| --- | --- | --- |
|
|
145
|
-
| hostname | <code>String</code> | hostname or ip of the WebSocket or OSC server |
|
|
146
|
-
| port | <code>Number</code> | listening port of the of the WebSocket or OSC server |
|
|
147
|
-
| autostart | <code>Boolean</code> | enable streaming on CoMo.te application |
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
<!-- apistop -->
|
|
151
|
-
|
|
152
|
-
## Credits
|
|
153
|
-
|
|
154
|
-
CoMo.te is developed by Ircam and the Music and Sound Science and Technology Joint Research Unit (STMS), supported by Ircam, CNRS, the French Ministry of Culture and Sorbonne University.
|
|
155
|
-
|
|
156
|
-
Produced with the support of the French Ministry of Education, Youth and Sports (Edu-up system), the National Research Agency (ELEMENT project), and in partnership with Radio France.
|
|
157
|
-
|
|
158
|
-
## License
|
|
159
|
-
|
|
160
|
-
BSD-3-Clause
|
|
9
|
+
Receive and parse sensor stream.
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ircam/comote-helpers",
|
|
3
|
-
"version": "0.3
|
|
4
|
-
"description": "Server component & utilities for the
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Server component & utilities for the CoMote application",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Benjamin.Matuszewski@ircam.fr",
|
|
7
7
|
"Jean-Philippe.Lambert@ircam.fr"
|
|
8
8
|
],
|
|
9
9
|
"license": "BSD-3-Clause",
|
|
10
|
+
"type": "module",
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
12
13
|
"url": "https://github.com/ircam-ismm/comote-helpers"
|
|
@@ -14,32 +15,15 @@
|
|
|
14
15
|
"publishConfig": {
|
|
15
16
|
"access": "public"
|
|
16
17
|
},
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"build": "npm run clean && babel src --out-dir .",
|
|
20
|
-
"clean": "rm -f server.js qrcode.js",
|
|
21
|
-
"dev": "npm run build && chokidar src -c \"npm run build\"",
|
|
22
|
-
"doc": "npm run api && npm run toc",
|
|
23
|
-
"prepublishOnly": "npm run build",
|
|
24
|
-
"toc": "markdown-toc -i README.md --maxdepth 3"
|
|
18
|
+
"exports": {
|
|
19
|
+
"./*.js": "./src/*.js"
|
|
25
20
|
},
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
"ws": "^8.6.0"
|
|
35
|
-
},
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"@babel/cli": "^7.4.4",
|
|
38
|
-
"@babel/core": "^7.4.5",
|
|
39
|
-
"@babel/preset-env": "^7.14.7",
|
|
40
|
-
"chokidar": "^3.0.1",
|
|
41
|
-
"chokidar-cli": "^2.1.0",
|
|
42
|
-
"jsdoc-to-readme": "^1.0.2",
|
|
43
|
-
"markdown-toc": "^1.2.0"
|
|
44
|
-
}
|
|
21
|
+
"files": [
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"workspaces": [
|
|
25
|
+
".",
|
|
26
|
+
"examples/*",
|
|
27
|
+
"max/CoMote/sources/*"
|
|
28
|
+
]
|
|
45
29
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import si from 'systeminformation';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} NetworkInterfaces
|
|
5
|
+
* @property {Array.<NetworkInterface>} Interfaces
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {Object} NetworkInterface
|
|
10
|
+
*
|
|
11
|
+
* @property {string} iface: - identifier eg. 'en0'
|
|
12
|
+
* @property {string} ifaceName - name eg. 'en0'
|
|
13
|
+
* @property {boolean} default - true if default interface (for sending data)
|
|
14
|
+
* @property {string} ip4 - address eg. '192.168.0.1'
|
|
15
|
+
* @property {string} ip4subnet - eg. '255.255.252.0'
|
|
16
|
+
* @property {string} ip6 eg. 'fe80::456:7890:abcd:ef01'
|
|
17
|
+
* @property {string} ip6subnet eg. 'ffff:ffff:ffff:ffff::'
|
|
18
|
+
* @property {string} type eg. 'wireless' or 'wired'
|
|
19
|
+
*
|
|
20
|
+
* @see https://www.npmjs.com/package/systeminformation
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Retrieve information of network interfaces
|
|
25
|
+
* Return `null` if no WiFi connection found.
|
|
26
|
+
*
|
|
27
|
+
* @async
|
|
28
|
+
* @return {NetworkInterfaces|null}
|
|
29
|
+
*/
|
|
30
|
+
export async function getNetworkInterfacesInfos({
|
|
31
|
+
interfaceFilter = (i) => {
|
|
32
|
+
// only IPv4
|
|
33
|
+
// avoid localhost
|
|
34
|
+
return i.ip4
|
|
35
|
+
&& i.ip4 !== '127.0.0.1'
|
|
36
|
+
&& i.ip4 !== 'localhost';
|
|
37
|
+
}
|
|
38
|
+
}= {}) {
|
|
39
|
+
const interfaces = await si.networkInterfaces();
|
|
40
|
+
|
|
41
|
+
if(!interfaces) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if(typeof interfaceFilter !== 'function') {
|
|
46
|
+
return interfaces;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return interfaces.filter(interfaceFilter);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import si from 'systeminformation';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} WifiInfos
|
|
5
|
+
* @property {number} ssid - SSID of the WiFi connection
|
|
6
|
+
* @property {number} ip - Related IP (IPV4)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Retrieve the SSID and related IP of the first WiFi connection found, return
|
|
11
|
+
* `null` if no WiFi connection found.
|
|
12
|
+
*
|
|
13
|
+
* @async
|
|
14
|
+
* @return {WiFiInfos|null}
|
|
15
|
+
*/
|
|
16
|
+
export async function getWifiInfos() {
|
|
17
|
+
// find first wifi connection
|
|
18
|
+
const wifiConnections = await si.wifiConnections();
|
|
19
|
+
const conn = wifiConnections[0];
|
|
20
|
+
|
|
21
|
+
if (!conn) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// find related interface
|
|
26
|
+
const interfaces = await si.networkInterfaces();
|
|
27
|
+
const int = interfaces.find(int => int.iface === conn.iface);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
ssid: conn.ssid,
|
|
31
|
+
ip: int.ip4,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.getNetworkInterfacesInfos = getNetworkInterfacesInfos;
|
|
7
|
-
var _systeminformation = _interopRequireDefault(require("systeminformation"));
|
|
8
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
1
|
+
import si from 'systeminformation';
|
|
2
|
+
|
|
9
3
|
/**
|
|
10
4
|
* @typedef {Object} NetworkInterfaces
|
|
11
5
|
* @property {Array.<NetworkInterface>} Interfaces
|
|
@@ -40,19 +34,34 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
40
34
|
* @param {interfacaFilterCallback} options.interfaceFilter callback for filter to test interface
|
|
41
35
|
* @return {NetworkInterfaces|null} null if no interface found
|
|
42
36
|
*/
|
|
43
|
-
async function getNetworkInterfacesInfos({
|
|
44
|
-
interfaceFilter = i => {
|
|
37
|
+
export async function getNetworkInterfacesInfos({
|
|
38
|
+
interfaceFilter = (i) => {
|
|
45
39
|
// only IPv4
|
|
46
40
|
// avoid localhost
|
|
47
|
-
return i.ip4
|
|
41
|
+
return i.ip4
|
|
42
|
+
&& i.ip4 !== '127.0.0.1'
|
|
43
|
+
&& i.ip4 !== 'localhost';
|
|
48
44
|
}
|
|
49
|
-
}
|
|
50
|
-
const interfaces = await
|
|
51
|
-
|
|
45
|
+
}= {}) {
|
|
46
|
+
const interfaces = await si.networkInterfaces();
|
|
47
|
+
|
|
48
|
+
if(!interfaces) {
|
|
52
49
|
return null;
|
|
53
50
|
}
|
|
54
|
-
|
|
51
|
+
|
|
52
|
+
if(typeof interfaceFilter !== 'function') {
|
|
55
53
|
return interfaces;
|
|
56
54
|
}
|
|
55
|
+
|
|
57
56
|
return interfaces.filter(interfaceFilter);
|
|
58
|
-
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
@@ -1,44 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.dataURL = dataURL;
|
|
7
|
-
exports.rawLink = rawLink;
|
|
8
|
-
exports.terminal = terminal;
|
|
9
|
-
var _qrcode = _interopRequireDefault(require("qrcode"));
|
|
10
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
1
|
+
import QRCode from 'qrcode';
|
|
2
|
+
|
|
11
3
|
function formatConfigToLink(config) {
|
|
12
|
-
let link = `comote://settings
|
|
4
|
+
let link = `comote://settings?`
|
|
13
5
|
const query = [];
|
|
6
|
+
|
|
14
7
|
if ('id' in config) {
|
|
15
8
|
query.push(`id=${config.id}`);
|
|
16
9
|
}
|
|
10
|
+
|
|
17
11
|
if (Number.isFinite(config.interval) && config.interval > 0) {
|
|
18
12
|
query.push(`interval=${config.interval}`);
|
|
19
13
|
}
|
|
14
|
+
|
|
20
15
|
if (config.ws) {
|
|
21
|
-
const {
|
|
22
|
-
protocol,
|
|
23
|
-
hostname,
|
|
24
|
-
port,
|
|
25
|
-
pathname
|
|
26
|
-
} = config.ws;
|
|
16
|
+
const { protocol, hostname, port, pathname } = config.ws;
|
|
27
17
|
|
|
28
18
|
// build valid url
|
|
29
19
|
// only hostname is required
|
|
30
20
|
if (!hostname) {
|
|
31
21
|
throw new Error(`Invalid WebSocket config: ${config.ws}`);
|
|
32
22
|
}
|
|
23
|
+
|
|
33
24
|
let url = `ws-url=`;
|
|
34
|
-
|
|
25
|
+
|
|
26
|
+
url += (protocol == 'ws' || protocol == 'wss') ? `${protocol}://` : `ws://`;
|
|
27
|
+
|
|
35
28
|
url += hostname;
|
|
29
|
+
|
|
36
30
|
if (port) {
|
|
37
31
|
url += `:${port}`;
|
|
38
32
|
}
|
|
33
|
+
|
|
39
34
|
if (pathname) {
|
|
40
35
|
url += pathname;
|
|
41
36
|
}
|
|
37
|
+
|
|
42
38
|
query.push(url);
|
|
43
39
|
|
|
44
40
|
// autostart
|
|
@@ -46,27 +42,34 @@ function formatConfigToLink(config) {
|
|
|
46
42
|
query.push(`ws-enable=1`);
|
|
47
43
|
}
|
|
48
44
|
}
|
|
45
|
+
|
|
49
46
|
if (config.osc) {
|
|
50
|
-
const {
|
|
51
|
-
|
|
52
|
-
port
|
|
53
|
-
} = config.osc;
|
|
47
|
+
const { hostname, port } = config.osc;
|
|
48
|
+
|
|
54
49
|
if (!hostname || !Number.isInteger(port)) {
|
|
55
50
|
throw new Error(`Invalid WebSocket config: ${config.osc}`);
|
|
56
51
|
}
|
|
52
|
+
|
|
57
53
|
query.push(`osc-url=udp://${hostname}:${port}`);
|
|
54
|
+
|
|
58
55
|
if (config.osc.autostart === true) {
|
|
59
56
|
query.push(`osc-enable=1`);
|
|
60
57
|
}
|
|
61
58
|
}
|
|
59
|
+
|
|
60
|
+
if (config.webview) {
|
|
61
|
+
query.push(`webview=${encodeURIComponent(config.webview)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
62
64
|
link += query.join('&');
|
|
65
|
+
|
|
63
66
|
return link;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
/**
|
|
67
70
|
* Return the link to be encoded in the QRCode accroding to given `CoMoteConfig`
|
|
68
71
|
*/
|
|
69
|
-
function rawLink(config) {
|
|
72
|
+
export function rawLink(config) {
|
|
70
73
|
return formatConfigToLink(config);
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -77,13 +80,12 @@ function rawLink(config) {
|
|
|
77
80
|
* @example
|
|
78
81
|
* console(await CoMoteQRCode.terminal(config));
|
|
79
82
|
*/
|
|
80
|
-
async function terminal(config) {
|
|
83
|
+
export async function terminal(config) {
|
|
81
84
|
const link = formatConfigToLink(config);
|
|
82
|
-
return await
|
|
83
|
-
type: 'terminal'
|
|
84
|
-
});
|
|
85
|
+
return await QRCode.toString(link, { type: 'terminal', small: true });
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
|
|
87
89
|
/**
|
|
88
90
|
* Create a qrcode to be used as in Image source according to given `CoMoteConfig``
|
|
89
91
|
*
|
|
@@ -93,7 +95,7 @@ async function terminal(config) {
|
|
|
93
95
|
*
|
|
94
96
|
* <img src="${qrCode}" />
|
|
95
97
|
*/
|
|
96
|
-
async function dataURL(config) {
|
|
98
|
+
export async function dataURL(config) {
|
|
97
99
|
const link = formatConfigToLink(config);
|
|
98
|
-
return await await
|
|
99
|
-
}
|
|
100
|
+
return await await QRCode.toDataURL(link);
|
|
101
|
+
}
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var _nodeOsc = require("node-osc");
|
|
10
|
-
var _cloneDeep = _interopRequireDefault(require("clone-deep"));
|
|
11
|
-
var _assignDeep = _interopRequireDefault(require("assign-deep"));
|
|
12
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
1
|
+
import { WebSocketServer } from 'ws';
|
|
2
|
+
import isValidUTF8 from 'utf-8-validate';
|
|
3
|
+
import { Server as OscServer } from 'node-osc';
|
|
4
|
+
import cloneDeep from 'clone-deep';
|
|
5
|
+
import assignDeep from 'assign-deep';
|
|
6
|
+
|
|
7
|
+
console.log(WebSocket);
|
|
8
|
+
|
|
13
9
|
/**
|
|
14
10
|
* @typedef {Object} CoMoteConfig
|
|
15
|
-
* @property {String} id - id of the client
|
|
16
|
-
* @property {Number} interval - period in ms of the sensors for the client
|
|
11
|
+
* @property {String} id - id of the client CoMote
|
|
12
|
+
* @property {Number} interval - period in ms of the sensors for the client CoMote
|
|
17
13
|
* @property {CoMoteTarget} osc - OSC configuration
|
|
18
14
|
* @property {CoMoteTarget} ws - WebSocket configuration
|
|
19
15
|
*/
|
|
@@ -22,9 +18,10 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
22
18
|
* @typedef {Object} CoMoteTarget
|
|
23
19
|
* @property {String} hostname - hostname or ip of the WebSocket or OSC server
|
|
24
20
|
* @property {Number} port - listening port of the of the WebSocket or OSC server
|
|
25
|
-
* @property {Boolean} autostart - enable streaming on
|
|
21
|
+
* @property {Boolean} autostart - enable streaming on CoMote application
|
|
26
22
|
*/
|
|
27
23
|
|
|
24
|
+
|
|
28
25
|
/**
|
|
29
26
|
* Launch WebSocket and/or OSC server according to given `CoMoteConfig` object
|
|
30
27
|
*
|
|
@@ -32,57 +29,60 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
32
29
|
* @param {Object} options - options
|
|
33
30
|
* @param {Object} [options.verbose=false] - logs debug informations
|
|
34
31
|
*/
|
|
35
|
-
class Server {
|
|
32
|
+
export class Server {
|
|
36
33
|
constructor(config, options) {
|
|
37
34
|
/**
|
|
38
35
|
* Configuration of the CoMote server
|
|
39
36
|
*/
|
|
40
|
-
this.config = (
|
|
37
|
+
this.config = cloneDeep(assignDeep({
|
|
41
38
|
id: null,
|
|
42
39
|
interval: null,
|
|
43
40
|
osc: null,
|
|
44
41
|
ws: null,
|
|
45
|
-
verbose: false
|
|
42
|
+
verbose: false,
|
|
46
43
|
}, config));
|
|
44
|
+
|
|
47
45
|
this._verbose = !!options.verbose;
|
|
46
|
+
|
|
48
47
|
if (this._verbose) {
|
|
49
|
-
console.log('+
|
|
48
|
+
console.log('+ CoMote config:');
|
|
50
49
|
console.log(this.config, '\n');
|
|
51
50
|
}
|
|
51
|
+
|
|
52
52
|
this._websocketServer = null;
|
|
53
53
|
this._oscServer = null;
|
|
54
|
+
|
|
54
55
|
this._wsListeners = new Set();
|
|
55
56
|
this._oscListeners = new Set();
|
|
56
57
|
}
|
|
58
|
+
|
|
57
59
|
async start() {
|
|
58
60
|
let wsPromise = true;
|
|
59
61
|
let oscPromise = true;
|
|
62
|
+
|
|
60
63
|
if (this.config.ws !== null) {
|
|
61
|
-
const {
|
|
62
|
-
|
|
63
|
-
port
|
|
64
|
-
} = this.config.ws;
|
|
64
|
+
const { hostname, port } = this.config.ws;
|
|
65
|
+
|
|
65
66
|
if (!Number.isInteger(port)) {
|
|
66
67
|
throw new Error(`Invalid port "${port}" for WebSocket server`);
|
|
67
68
|
}
|
|
69
|
+
|
|
68
70
|
if (this._verbose) {
|
|
69
|
-
console.log(`>
|
|
71
|
+
console.log(`> CoMote: Launching WebSocket server on port: ${port}`);
|
|
70
72
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
const sockets = new Map();
|
|
73
|
+
|
|
74
|
+
this._websocketServer = new WebSocketServer({ port });
|
|
75
75
|
this._websocketServer.on('connection', (socket, request) => {
|
|
76
76
|
// const ip = request.socket.remoteAddress;
|
|
77
77
|
socket.on('message', (data, isBinary) => {
|
|
78
78
|
if (isBinary) {
|
|
79
79
|
// @todo
|
|
80
80
|
} else {
|
|
81
|
-
if ((
|
|
82
|
-
// do we really need this check?
|
|
81
|
+
if (isValidUTF8(data)) { // do we really need this check?
|
|
83
82
|
data = JSON.parse(data);
|
|
83
|
+
|
|
84
84
|
if (this._verbose) {
|
|
85
|
-
console.log(`>
|
|
85
|
+
console.log(`> CoMote: new WebSocket message`, data);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// console.log(data);
|
|
@@ -95,28 +95,30 @@ class Server {
|
|
|
95
95
|
// When a socket closes, or disconnects, remove it from the array.
|
|
96
96
|
socket.on('close', (code, data) => {
|
|
97
97
|
if (this._verbose) {
|
|
98
|
-
console.log('>
|
|
98
|
+
console.log('> CoMote: closed socket connection');
|
|
99
99
|
}
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
|
+
|
|
102
103
|
wsPromise = new Promise((resolve, reject) => {
|
|
103
104
|
this._websocketServer.on('listening', () => {
|
|
104
105
|
if (this._verbose) {
|
|
105
|
-
console.log(`>
|
|
106
|
+
console.log(`> CoMote: WebSocket server listening`);
|
|
106
107
|
}
|
|
108
|
+
|
|
107
109
|
resolve();
|
|
108
110
|
});
|
|
111
|
+
|
|
109
112
|
this._websocketServer.on('error', err => {
|
|
110
|
-
console.log(`>
|
|
113
|
+
console.log(`> CoMote: WebSocket server error`, err);
|
|
111
114
|
reject(err);
|
|
112
115
|
});
|
|
113
116
|
});
|
|
114
117
|
}
|
|
118
|
+
|
|
115
119
|
if (this.config.osc !== null) {
|
|
116
|
-
const {
|
|
117
|
-
|
|
118
|
-
port
|
|
119
|
-
} = this.config.osc;
|
|
120
|
+
const { hostname, port } = this.config.osc;
|
|
121
|
+
|
|
120
122
|
if (!Number.isInteger(port)) {
|
|
121
123
|
throw new Error(`Invalid port "${port}" for OSC server`);
|
|
122
124
|
}
|
|
@@ -125,46 +127,57 @@ class Server {
|
|
|
125
127
|
if (!hostname) {
|
|
126
128
|
hostname = '0.0.0.0';
|
|
127
129
|
}
|
|
130
|
+
|
|
128
131
|
if (this._verbose) {
|
|
129
|
-
console.log(`>
|
|
132
|
+
console.log(`> CoMote: Launching OSC server udp://${hostname}:${port}`);
|
|
130
133
|
}
|
|
134
|
+
|
|
131
135
|
oscPromise = new Promise((resolve, reject) => {
|
|
132
|
-
this._oscServer = new
|
|
136
|
+
this._oscServer = new OscServer(this.config.osc.port, hostname, err => {
|
|
133
137
|
if (err) {
|
|
134
|
-
console.log(`>
|
|
138
|
+
console.log(`> CoMote: OSC server error`, err);
|
|
135
139
|
reject();
|
|
136
140
|
return;
|
|
137
141
|
}
|
|
142
|
+
|
|
138
143
|
if (this._verbose) {
|
|
139
|
-
console.log(`>
|
|
144
|
+
console.log(`> CoMote: OSC server listening`);
|
|
140
145
|
}
|
|
146
|
+
|
|
141
147
|
resolve();
|
|
142
148
|
});
|
|
149
|
+
|
|
143
150
|
this._oscServer.on('message', data => {
|
|
144
151
|
let address = data.shift();
|
|
152
|
+
|
|
145
153
|
if (this._verbose) {
|
|
146
|
-
console.log(`>
|
|
154
|
+
console.log(`> CoMote: new OSC message "${address}":`, data);
|
|
147
155
|
}
|
|
156
|
+
|
|
148
157
|
this._oscListeners.forEach(callback => callback(address, data));
|
|
149
158
|
});
|
|
150
159
|
});
|
|
151
160
|
}
|
|
161
|
+
|
|
152
162
|
return Promise.all([wsPromise, oscPromise]);
|
|
153
163
|
}
|
|
164
|
+
|
|
154
165
|
async stop() {
|
|
155
166
|
if (this._websocketServer) {
|
|
156
167
|
this._websocketServer.close();
|
|
157
168
|
}
|
|
169
|
+
|
|
158
170
|
if (this._oscServer) {
|
|
159
171
|
this._oscServer.close();
|
|
160
172
|
}
|
|
161
173
|
}
|
|
162
174
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Add a listener for incomming WebSocket message
|
|
177
|
+
*/
|
|
166
178
|
addWsListener(callback) {
|
|
167
179
|
this._wsListeners.add(callback);
|
|
180
|
+
|
|
168
181
|
return () => this._wsListeners.delete(callback);
|
|
169
182
|
}
|
|
170
183
|
|
|
@@ -180,6 +193,7 @@ class Server {
|
|
|
180
193
|
*/
|
|
181
194
|
addOscListener(callback) {
|
|
182
195
|
this._oscListeners.add(callback);
|
|
196
|
+
|
|
183
197
|
return () => this._oscListeners.delete(callback);
|
|
184
198
|
}
|
|
185
199
|
|
|
@@ -190,4 +204,3 @@ class Server {
|
|
|
190
204
|
this._oscListeners.delete(callback);
|
|
191
205
|
}
|
|
192
206
|
}
|
|
193
|
-
exports.Server = Server;
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import si from 'systeminformation';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.getWifiInfos = getWifiInfos;
|
|
7
|
-
var _systeminformation = _interopRequireDefault(require("systeminformation"));
|
|
8
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
9
3
|
/**
|
|
10
4
|
* @typedef {Object} WifiInfos
|
|
11
5
|
* @property {number} ssid - SSID of the WiFi connection
|
|
@@ -19,19 +13,21 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
19
13
|
* @async
|
|
20
14
|
* @return {WiFiInfos|null}
|
|
21
15
|
*/
|
|
22
|
-
async function getWifiInfos() {
|
|
16
|
+
export async function getWifiInfos() {
|
|
23
17
|
// find first wifi connection
|
|
24
|
-
const wifiConnections = await
|
|
18
|
+
const wifiConnections = await si.wifiConnections();
|
|
25
19
|
const conn = wifiConnections[0];
|
|
20
|
+
|
|
26
21
|
if (!conn) {
|
|
27
22
|
return null;
|
|
28
23
|
}
|
|
29
24
|
|
|
30
25
|
// find related interface
|
|
31
|
-
const interfaces = await
|
|
26
|
+
const interfaces = await si.networkInterfaces();
|
|
32
27
|
const int = interfaces.find(int => int.iface === conn.iface);
|
|
28
|
+
|
|
33
29
|
return {
|
|
34
30
|
ssid: conn.ssid,
|
|
35
|
-
ip: int.ip4
|
|
31
|
+
ip: int.ip4,
|
|
36
32
|
};
|
|
37
|
-
}
|
|
33
|
+
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Zip and release Max package on new version
|
|
2
|
-
|
|
3
|
-
name: CI
|
|
4
|
-
|
|
5
|
-
# Controls when the action will run.
|
|
6
|
-
on:
|
|
7
|
-
# Triggers the workflow on push or pull request events but only for the master branch
|
|
8
|
-
push:
|
|
9
|
-
# Sequence of patterns matched against refs/tags
|
|
10
|
-
tags:
|
|
11
|
-
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
|
12
|
-
|
|
13
|
-
# Allows you to run this workflow manually from the Actions tab
|
|
14
|
-
workflow_dispatch:
|
|
15
|
-
|
|
16
|
-
jobs:
|
|
17
|
-
build:
|
|
18
|
-
name: Upload Release Asset
|
|
19
|
-
runs-on: ubuntu-latest
|
|
20
|
-
steps:
|
|
21
|
-
- name: Checkout code
|
|
22
|
-
uses: actions/checkout@v2
|
|
23
|
-
- name: Build project # This would actually build your project, using zip for an example artifact
|
|
24
|
-
run: |
|
|
25
|
-
(cd max && zip -r - CoMo.te) > CoMo.te.zip
|
|
26
|
-
- name: Create Release
|
|
27
|
-
id: create_release
|
|
28
|
-
uses: actions/create-release@v1
|
|
29
|
-
env:
|
|
30
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
31
|
-
with:
|
|
32
|
-
tag_name: ${{ github.ref }}
|
|
33
|
-
release_name: Release ${{ github.ref }}
|
|
34
|
-
draft: false
|
|
35
|
-
prerelease: false
|
|
36
|
-
- name: Upload Release Asset
|
|
37
|
-
id: upload-release-asset
|
|
38
|
-
uses: actions/upload-release-asset@v1
|
|
39
|
-
env:
|
|
40
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
41
|
-
with:
|
|
42
|
-
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
|
43
|
-
asset_path: ./CoMo.te.zip
|
|
44
|
-
asset_name: CoMo.te.zip
|
|
45
|
-
asset_content_type: application/zip
|