node-red-contrib-yandex-station-management 0.3.3 → 0.3.4
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/lib/stationHelper.js +52 -0
- package/nodes/get.js +8 -34
- package/nodes/in.js +3 -37
- package/nodes/yandex-login.js +150 -113
- package/package.json +1 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class stationHelper {
|
|
4
|
+
|
|
5
|
+
static preparePayload(node, message) {
|
|
6
|
+
let payload = {};
|
|
7
|
+
if (node.output == 'status') {
|
|
8
|
+
payload = { 'payload': message }
|
|
9
|
+
} else if (node.output == 'homekit') {
|
|
10
|
+
let playing = false;
|
|
11
|
+
if (typeof(message.playing) !== 'undefined') {
|
|
12
|
+
playing = message.playing;
|
|
13
|
+
}
|
|
14
|
+
if (node.homekitFormat == 'speaker') {
|
|
15
|
+
let subtitle = 'No Artist';
|
|
16
|
+
let title = 'No Track Name';
|
|
17
|
+
|
|
18
|
+
if (typeof(message.playerState) !== 'undefined') {
|
|
19
|
+
let playerState = message.playerState;
|
|
20
|
+
if (typeof(playerState.subtitle) !== 'undefined') {
|
|
21
|
+
subtitle = playerState.subtitle;
|
|
22
|
+
}
|
|
23
|
+
if (typeof(playerState.title) !== 'undefined') {
|
|
24
|
+
title = playerState.title;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let ConfiguredName = `${subtitle} - ${title}`;
|
|
29
|
+
if (ConfiguredName.length > 64) {
|
|
30
|
+
ConfiguredName = title.length <= 64 ? title : title.substr(0, 61) + `...`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
payload = {
|
|
34
|
+
'payload': {
|
|
35
|
+
'CurrentMediaState': (playing) ? 0 : 1,
|
|
36
|
+
'ConfiguredName': ConfiguredName
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} else if (node.homekitFormat == 'tv') {
|
|
40
|
+
payload = {
|
|
41
|
+
'payload': {
|
|
42
|
+
'Active': (playing) ? 1 : 0
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return payload;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = stationHelper;
|
package/nodes/get.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
|
+
const stationHelper = require('../lib/stationHelper.js');
|
|
3
|
+
|
|
2
4
|
function AliceLocalGetNode(config) {
|
|
3
5
|
RED.nodes.createNode(this,config);
|
|
4
6
|
let node = this;
|
|
@@ -16,43 +18,15 @@ module.exports = function(RED) {
|
|
|
16
18
|
node.log(text);
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
|
-
function preparePayload(message,inputMsg){
|
|
20
|
-
//let payload = {};
|
|
21
|
-
if (node.output == 'status') {
|
|
22
|
-
inputMsg.payload = message;
|
|
23
|
-
} else if (node.output == 'homekit') {
|
|
24
|
-
try {
|
|
25
|
-
if (node.homekitFormat == 'speaker') {
|
|
26
|
-
let ConfiguredName = `${(message.playerState.subtitle) ? message.playerState.subtitle : 'No Artist'} - ${(message.playerState.title) ? message.playerState.title : 'No Track Name'}`;
|
|
27
|
-
let title = `${message.playerState.title}`;
|
|
28
|
-
if (ConfiguredName.length > 64 && title.length > 0 && title.length <= 64) {
|
|
29
|
-
ConfiguredName = title;
|
|
30
|
-
} else {
|
|
31
|
-
ConfiguredName = title.substr(0, 61) + `...`;
|
|
32
|
-
}
|
|
33
|
-
(message.playerState)? inputMsg.payload = {
|
|
34
|
-
"CurrentMediaState": (message.playing) ? 0 : 1,
|
|
35
|
-
"ConfiguredName": ConfiguredName
|
|
36
|
-
} :inputMsg.payload = {
|
|
37
|
-
"CurrentMediaState": (message.playing) ? 0 : 1,
|
|
38
|
-
"ConfiguredName": `No Artist - No Track Name`
|
|
39
|
-
}
|
|
40
|
-
}else if (node.homekitFormat == 'tv') {
|
|
41
|
-
inputMsg.payload = {
|
|
42
|
-
"Active": (message.playing) ? 1 : 0
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
} catch (error) {
|
|
47
|
-
debugMessage(`Error while preparing payload: `+ e);
|
|
48
|
-
}
|
|
49
21
|
|
|
22
|
+
function _preparePayload(message, inputMsg) {
|
|
23
|
+
let prepare = stationHelper.preparePayload(node, message);
|
|
24
|
+
if (typeof(prepare.payload) !== 'undefined') {
|
|
25
|
+
inputMsg.payload = prepare.payload;
|
|
50
26
|
}
|
|
51
|
-
|
|
52
|
-
|
|
27
|
+
return inputMsg;
|
|
53
28
|
}
|
|
54
29
|
|
|
55
|
-
|
|
56
30
|
node.onStatus = function(data) {
|
|
57
31
|
if (data) {
|
|
58
32
|
node.status({fill: `${data.color}`,shape:"dot",text: `${data.text}`});
|
|
@@ -61,7 +35,7 @@ module.exports = function(RED) {
|
|
|
61
35
|
}
|
|
62
36
|
node.onInput = function(msg, send, done){
|
|
63
37
|
debugMessage('current state: ' + JSON.stringify(node.lastState));
|
|
64
|
-
( 'aliceState' in node.lastState )?node.send(
|
|
38
|
+
( 'aliceState' in node.lastState )?node.send(_preparePayload(node.lastState,msg)):node.send(msg)
|
|
65
39
|
}
|
|
66
40
|
node.onMessage = function(message){
|
|
67
41
|
node.lastState = message;
|
package/nodes/in.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
|
+
const stationHelper = require('../lib/stationHelper.js');
|
|
3
|
+
|
|
2
4
|
function AliceLocalInNode(config) {
|
|
3
5
|
RED.nodes.createNode(this,config);
|
|
4
6
|
let node = this;
|
|
@@ -20,42 +22,6 @@ module.exports = function(RED) {
|
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
function preparePayload(message){
|
|
25
|
-
let payload = {};
|
|
26
|
-
if (node.output == 'status') {
|
|
27
|
-
payload = {'payload': message}
|
|
28
|
-
} else if (node.output == 'homekit') {
|
|
29
|
-
try {
|
|
30
|
-
if (node.homekitFormat == 'speaker') {
|
|
31
|
-
let ConfiguredName = `${(message.playerState.subtitle) ? message.playerState.subtitle : 'No Artist'} - ${(message.playerState.title) ? message.playerState.title : 'No Track Name'}`;
|
|
32
|
-
let title = `${message.playerState.title}`;
|
|
33
|
-
if (ConfiguredName.length > 64 && title.length > 0 && title.length <= 64) {
|
|
34
|
-
ConfiguredName = title;
|
|
35
|
-
} else {
|
|
36
|
-
ConfiguredName = title.substr(0, 61) + `...`;
|
|
37
|
-
}
|
|
38
|
-
(message.playerState)? payload = {'payload': {
|
|
39
|
-
"CurrentMediaState": (message.playing) ? 0 : 1,
|
|
40
|
-
"ConfiguredName": ConfiguredName
|
|
41
|
-
} }:payload = {'payload': {
|
|
42
|
-
"CurrentMediaState": (message.playing) ? 0 : 1,
|
|
43
|
-
"ConfiguredName": `No Artists - No Track Name`
|
|
44
|
-
} }
|
|
45
|
-
}else if (node.homekitFormat == 'tv') {
|
|
46
|
-
payload = {'payload': {
|
|
47
|
-
"Active": (message.playing) ? 1 : 0
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
} catch(e) {
|
|
52
|
-
debugMessage(`Error while preparing payload: `+ e)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
return payload;
|
|
57
|
-
|
|
58
|
-
}
|
|
59
25
|
function sendMessage(message){
|
|
60
26
|
//debugMessage(JSON.stringify(message));
|
|
61
27
|
if (node.uniqueFlag && node.output == 'homekit') {
|
|
@@ -71,7 +37,7 @@ module.exports = function(RED) {
|
|
|
71
37
|
}
|
|
72
38
|
node.onMessage = function(data){
|
|
73
39
|
//debugMessage(JSON.stringify(data));
|
|
74
|
-
sendMessage(preparePayload(data));
|
|
40
|
+
sendMessage(stationHelper.preparePayload(node, data));
|
|
75
41
|
}
|
|
76
42
|
node.onStatus = function(data) {
|
|
77
43
|
if (data) {
|
package/nodes/yandex-login.js
CHANGED
|
@@ -18,27 +18,32 @@ module.exports = function(RED) {
|
|
|
18
18
|
node.readyList = [];
|
|
19
19
|
node.activeStationList = [];
|
|
20
20
|
//node.skipCloudDevices = false;
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
|
|
22
|
+
|
|
23
23
|
node.on('stopListening', onStopListening);
|
|
24
24
|
node.on('startPlay', onStartPlay);
|
|
25
25
|
node.on('stopPlay', onStopPlay);
|
|
26
26
|
node.on('setVolume', onSetVolume);
|
|
27
27
|
node.on('deviceReady', onDeviceReady);
|
|
28
28
|
node.setMaxListeners(0)
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
function debugMessage(text){
|
|
31
31
|
if (node.debugFlag) {
|
|
32
|
-
|
|
32
|
+
try {
|
|
33
|
+
node.log(text);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
node.log(error)
|
|
36
|
+
}
|
|
37
|
+
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
|
-
|
|
40
|
+
|
|
36
41
|
let registrationBuffer = [];
|
|
37
|
-
|
|
42
|
+
|
|
38
43
|
function deviceListProcessing(deviceList) {
|
|
39
44
|
deviceList.forEach(device => {
|
|
40
45
|
if (device.address && device.port ) {
|
|
41
|
-
|
|
46
|
+
|
|
42
47
|
if (node.readyList.find(item => item.id == device.id)){
|
|
43
48
|
//debugMessage('skipping');
|
|
44
49
|
} else {
|
|
@@ -54,23 +59,23 @@ module.exports = function(RED) {
|
|
|
54
59
|
|
|
55
60
|
async function getDevices(token)
|
|
56
61
|
{
|
|
57
|
-
let options =
|
|
58
|
-
{
|
|
62
|
+
let options =
|
|
63
|
+
{
|
|
59
64
|
method: 'GET',
|
|
60
65
|
url: 'https://quasar.yandex.net/glagol/device_list',
|
|
61
|
-
headers:
|
|
62
|
-
{
|
|
66
|
+
headers:
|
|
67
|
+
{
|
|
63
68
|
'Content-Type': 'application/json',
|
|
64
|
-
'Authorization': 'Oauth ' + token
|
|
65
|
-
}
|
|
69
|
+
'Authorization': 'Oauth ' + token
|
|
70
|
+
}
|
|
66
71
|
};
|
|
67
|
-
// вариант для снижения частоты запросов на серверы ЯНдекса для обновления списка устройств. Требует тестирования.
|
|
72
|
+
// вариант для снижения частоты запросов на серверы ЯНдекса для обновления списка устройств. Требует тестирования.
|
|
68
73
|
/* if (node.skipCloudDevices) {
|
|
69
74
|
discoverDevices(node.deviceList)
|
|
70
75
|
.then(() => {
|
|
71
76
|
//debugMessage(`calling processing for ${node.deviceList.length} devices`);
|
|
72
77
|
deviceListProcessing(node.deviceList)
|
|
73
|
-
|
|
78
|
+
|
|
74
79
|
});
|
|
75
80
|
return node.deviceList;
|
|
76
81
|
} */
|
|
@@ -91,7 +96,7 @@ module.exports = function(RED) {
|
|
|
91
96
|
if (result != 2 && result != undefined) {
|
|
92
97
|
registrationBuffer.splice(registrationBuffer.indexOf(bufferStation,1));
|
|
93
98
|
}
|
|
94
|
-
|
|
99
|
+
|
|
95
100
|
}
|
|
96
101
|
node.activeStationList.push({ 'name': device.name, 'id': device.id, 'platform': device.platform, 'address': device.address, 'port': device.port});
|
|
97
102
|
});
|
|
@@ -102,13 +107,13 @@ module.exports = function(RED) {
|
|
|
102
107
|
.then(() => {
|
|
103
108
|
//debugMessage(`calling processing for ${node.deviceList.length} devices`);
|
|
104
109
|
deviceListProcessing(node.deviceList)
|
|
105
|
-
|
|
110
|
+
|
|
106
111
|
});
|
|
107
112
|
//debugMessage(node.id);
|
|
108
113
|
return node.deviceList;
|
|
109
114
|
} catch (error) {
|
|
110
115
|
debugMessage(`Error while searching: ${error}`);
|
|
111
|
-
}
|
|
116
|
+
}
|
|
112
117
|
|
|
113
118
|
})
|
|
114
119
|
.catch(function (err) {
|
|
@@ -126,7 +131,6 @@ module.exports = function(RED) {
|
|
|
126
131
|
await mDnsSd.discover({
|
|
127
132
|
name: '_yandexio._tcp.local'
|
|
128
133
|
}).then((result) => {
|
|
129
|
-
debugMessage(`MDNS. Found ${result.length} devices`);
|
|
130
134
|
node.emit('refreshHttpDNS', result);
|
|
131
135
|
if (result.length != 0){
|
|
132
136
|
for (const device of deviceList) {
|
|
@@ -147,15 +151,15 @@ module.exports = function(RED) {
|
|
|
147
151
|
} catch(e) {
|
|
148
152
|
debugMessage(`Error searching hostname in mDNS answer`)
|
|
149
153
|
}
|
|
150
|
-
|
|
154
|
+
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
|
-
}
|
|
157
|
+
}
|
|
154
158
|
})
|
|
155
|
-
|
|
159
|
+
|
|
156
160
|
}
|
|
157
161
|
}
|
|
158
|
-
|
|
162
|
+
|
|
159
163
|
}).catch(function (err) {
|
|
160
164
|
debugMessage(err);
|
|
161
165
|
});
|
|
@@ -172,15 +176,15 @@ module.exports = function(RED) {
|
|
|
172
176
|
|
|
173
177
|
async function getLocalToken(device) {
|
|
174
178
|
let data;
|
|
175
|
-
let options = {
|
|
179
|
+
let options = {
|
|
176
180
|
method: 'GET',
|
|
177
181
|
url: 'https://quasar.yandex.net/glagol/token',
|
|
178
182
|
qs: { device_id: device.id, platform: device.platform },
|
|
179
|
-
headers:
|
|
180
|
-
{
|
|
183
|
+
headers:
|
|
184
|
+
{
|
|
181
185
|
'Authorization': 'Oauth ' + node.token,
|
|
182
|
-
'Content-Type': 'application/json'
|
|
183
|
-
}
|
|
186
|
+
'Content-Type': 'application/json'
|
|
187
|
+
}
|
|
184
188
|
};
|
|
185
189
|
// debugMessage(JSON.stringify(options))
|
|
186
190
|
statusUpdate({"color": "yellow", "text": "connecting..."}, device);
|
|
@@ -188,7 +192,8 @@ module.exports = function(RED) {
|
|
|
188
192
|
.then(function(response)
|
|
189
193
|
{
|
|
190
194
|
data = JSON.parse(response);
|
|
191
|
-
device.token = data.token
|
|
195
|
+
device.token = data.token;
|
|
196
|
+
debugMessage(`${device.id}: Recieved conversation new token`)
|
|
192
197
|
})
|
|
193
198
|
.catch(function (err) {
|
|
194
199
|
removeDevice(node.readyList, device);
|
|
@@ -208,12 +213,12 @@ module.exports = function(RED) {
|
|
|
208
213
|
connect(device)
|
|
209
214
|
};
|
|
210
215
|
|
|
211
|
-
|
|
216
|
+
|
|
212
217
|
function connect(device) {
|
|
213
218
|
//connect only if !device.ws
|
|
214
219
|
//debugMessage(`device.ws = ${JSON.stringify(device.ws)}`);
|
|
215
220
|
if ( (device.connection == true || typeof(device.connection) == "undefined") && node.listenerCount(`statusUpdate_${device.id}`) > 0 ) {
|
|
216
|
-
debugMessage(`Connecting to device ${device.id}. ws is ${
|
|
221
|
+
debugMessage(`Connecting to device ${device.id}. ws is ${device.ws}. Listeners: ` + node.listenerCount(`statusUpdate_${device.id}`));
|
|
217
222
|
if (!device.ws) {
|
|
218
223
|
debugMessage('recieving conversation token...');
|
|
219
224
|
getLocalToken(device)
|
|
@@ -226,7 +231,7 @@ module.exports = function(RED) {
|
|
|
226
231
|
})
|
|
227
232
|
.catch(function (err) {
|
|
228
233
|
debugMessage('Error while getting token: ' + err);
|
|
229
|
-
|
|
234
|
+
|
|
230
235
|
});
|
|
231
236
|
} else {
|
|
232
237
|
if (device.ws.readyState == 3) {
|
|
@@ -258,16 +263,16 @@ module.exports = function(RED) {
|
|
|
258
263
|
//}
|
|
259
264
|
}
|
|
260
265
|
} else {
|
|
261
|
-
debugMessage(`${device.id} connection is disabled by settings in manager node ${device.manager} or you have not use any node for this station`)
|
|
262
|
-
statusUpdate({"color": "red", "text": "disconnected"}, device);
|
|
266
|
+
//debugMessage(`${device.id} connection is disabled by settings in manager node ${device.manager} or you have not use any node for this station`)
|
|
267
|
+
//statusUpdate({"color": "red", "text": "disconnected"}, device);
|
|
263
268
|
device.timer = setTimeout(connect, 60000, device);
|
|
264
269
|
|
|
265
270
|
}
|
|
266
271
|
|
|
267
272
|
}
|
|
268
|
-
|
|
273
|
+
|
|
269
274
|
async function makeConn(device) {
|
|
270
|
-
|
|
275
|
+
|
|
271
276
|
let options = {
|
|
272
277
|
key: device.glagol.security.server_private_key,
|
|
273
278
|
cert: device.glagol.security.server_certificate,
|
|
@@ -276,7 +281,9 @@ module.exports = function(RED) {
|
|
|
276
281
|
device.lastState = {};
|
|
277
282
|
debugMessage(`Connecting to wss://${device.address}:${device.port}`);
|
|
278
283
|
device.ws = new WebSocket(`wss://${device.address}:${device.port}`, options);
|
|
279
|
-
device.
|
|
284
|
+
debugMessage(`${device.id}: Fire connection watchdog for 60 seconds`);
|
|
285
|
+
device.watchDogConn = setTimeout(() => {reconnect(device)}, 60000);
|
|
286
|
+
device.ws.on('open', function open(data) {
|
|
280
287
|
debugMessage(`Connected to ${device.address}, data: ${data}`);
|
|
281
288
|
sendMessage(device.id, 'command', {payload: 'ping'});
|
|
282
289
|
statusUpdate({"color": "green", "text": "connected"}, device);
|
|
@@ -286,13 +293,15 @@ module.exports = function(RED) {
|
|
|
286
293
|
device.waitForIdle = false;
|
|
287
294
|
device.watchDog = setTimeout(() => device.ws.close(), 10000);
|
|
288
295
|
device.pingInterval = setInterval(onPing,300,device);
|
|
296
|
+
debugMessage(`${device.id}: Kill connection watchdog`);
|
|
297
|
+
clearTimeout(device.watchDogConn);
|
|
289
298
|
clearTimeout(device.timer);
|
|
290
299
|
debugMessage(`readyState: ${device.ws.readyState}`)
|
|
291
300
|
});
|
|
292
301
|
device.ws.on('message', function incoming(data) {
|
|
293
302
|
//debugMessage(`${device.id}: ${JSON.stringify(data)}`);
|
|
294
303
|
let dataRecieved = JSON.parse(data);
|
|
295
|
-
device.lastState = dataRecieved.state;
|
|
304
|
+
device.lastState = dataRecieved.state;
|
|
296
305
|
device.fullMessage = JSON.stringify(dataRecieved);
|
|
297
306
|
//debugMessage(checkSheduler(device, JSON.parse(data).sentTime));
|
|
298
307
|
node.emit(`message_${device.id}`, device.lastState);
|
|
@@ -312,7 +321,7 @@ module.exports = function(RED) {
|
|
|
312
321
|
if (device.lastState.playing && device.lastState.aliceState != 'LISTENING' && device.parameters.hasOwnProperty("sheduler")) {
|
|
313
322
|
let res = checkSheduler(device, dataRecieved.sentTime)
|
|
314
323
|
//debugMessage(`Result of cheking sheduler is ${res.toString}`);
|
|
315
|
-
if (!res[0]) {
|
|
324
|
+
if (!res[0]) {
|
|
316
325
|
if (device.shedulerFlag || device.shedulerFlag == undefined) {
|
|
317
326
|
node.emit('stopPlay', device, res[1])
|
|
318
327
|
device.shedulerFlag = false
|
|
@@ -324,7 +333,7 @@ module.exports = function(RED) {
|
|
|
324
333
|
clearTimeout(device.watchDog);
|
|
325
334
|
//debugMessage(`cleared timeout for ${device.id}`)
|
|
326
335
|
device.watchDog = setTimeout(() => {device.ws.close()}, 10000);
|
|
327
|
-
});
|
|
336
|
+
});
|
|
328
337
|
//device.ws.on('ping', function);
|
|
329
338
|
device.ws.on('close', function close(code, reason){
|
|
330
339
|
statusUpdate({"color": "red", "text": "disconnected"}, device);
|
|
@@ -336,15 +345,15 @@ module.exports = function(RED) {
|
|
|
336
345
|
debugMessage(`getting new token...`);
|
|
337
346
|
connect(device);
|
|
338
347
|
break;
|
|
339
|
-
case 1000:
|
|
348
|
+
case 1000:
|
|
340
349
|
debugMessage(`Closed connection code ${code} with reason ${reason}. Reconnecting...` );
|
|
341
350
|
connect(device);
|
|
342
|
-
break;
|
|
351
|
+
break;
|
|
343
352
|
case 1006:
|
|
344
353
|
debugMessage(`Lost server, reconnect in 60 seconds...${code} + ${reason}` );
|
|
345
354
|
device.timer = setTimeout(connect, 60000, device);
|
|
346
355
|
break;
|
|
347
|
-
|
|
356
|
+
|
|
348
357
|
case 10000:
|
|
349
358
|
debugMessage(`Reconnect device reason 10000 ${device.id}`);
|
|
350
359
|
connect(device);
|
|
@@ -356,7 +365,7 @@ module.exports = function(RED) {
|
|
|
356
365
|
}
|
|
357
366
|
|
|
358
367
|
|
|
359
|
-
})
|
|
368
|
+
})
|
|
360
369
|
device.ws.on('error', function error(data){
|
|
361
370
|
//statusUpdate({"color": "red", "text": "disconnected"}, device);
|
|
362
371
|
debugMessage(`error: ${data}`);
|
|
@@ -417,7 +426,7 @@ module.exports = function(RED) {
|
|
|
417
426
|
return [{
|
|
418
427
|
"command": "rewind",
|
|
419
428
|
"position": 0
|
|
420
|
-
}]
|
|
429
|
+
}]
|
|
421
430
|
}
|
|
422
431
|
} else if (message.payload == 'volumeup') {
|
|
423
432
|
debugMessage(currentVolume);
|
|
@@ -425,7 +434,7 @@ module.exports = function(RED) {
|
|
|
425
434
|
return [{
|
|
426
435
|
"command": "setVolume",
|
|
427
436
|
"volume": currentVolume + 0.1
|
|
428
|
-
}]
|
|
437
|
+
}]
|
|
429
438
|
}
|
|
430
439
|
} else if (message.payload == 'volumedown') {
|
|
431
440
|
debugMessage(currentVolume);
|
|
@@ -433,13 +442,13 @@ module.exports = function(RED) {
|
|
|
433
442
|
return [{
|
|
434
443
|
"command": "setVolume",
|
|
435
444
|
"volume": currentVolume - 0.1
|
|
436
|
-
}]
|
|
445
|
+
}]
|
|
437
446
|
}
|
|
438
447
|
} else if (message.payload == 'volume') {
|
|
439
448
|
return [{
|
|
440
449
|
"command": "setVolume",
|
|
441
450
|
"volume": parseFloat(message.level)
|
|
442
|
-
}]
|
|
451
|
+
}]
|
|
443
452
|
}
|
|
444
453
|
|
|
445
454
|
} else {
|
|
@@ -447,7 +456,7 @@ module.exports = function(RED) {
|
|
|
447
456
|
//node.error(`You can send commands in msg.payload from list as String ${commands + extraCommands}`);
|
|
448
457
|
return [{"command": "ping"}];
|
|
449
458
|
}
|
|
450
|
-
case 'voice':
|
|
459
|
+
case 'voice':
|
|
451
460
|
debugMessage(`Message Voice command: ${message}`);
|
|
452
461
|
return [{
|
|
453
462
|
"command" : "sendText",
|
|
@@ -515,56 +524,84 @@ module.exports = function(RED) {
|
|
|
515
524
|
return result;
|
|
516
525
|
break;
|
|
517
526
|
case 'homekit':
|
|
518
|
-
debugMessage('HAP: ' + JSON.stringify(message) + ' PL: ' + JSON.stringify(message.payload) );
|
|
527
|
+
debugMessage('HAP: ' + JSON.stringify(message) + ' PL: ' + JSON.stringify(message.payload) );
|
|
519
528
|
if ("session" in message.hap) {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
return messageConstructor('voice', {'payload': message.noTrackPhrase})
|
|
533
|
-
} else {
|
|
534
|
-
return messageConstructor('command', {'payload': 'ping'})
|
|
529
|
+
let playing = false;
|
|
530
|
+
let id = null;
|
|
531
|
+
let noTrackPhrase = message.noTrackPhrase;
|
|
532
|
+
|
|
533
|
+
if (typeof (device.lastState) !== 'undefined') {
|
|
534
|
+
let lastState = device.lastState;
|
|
535
|
+
if (typeof (lastState.playing) !== 'undefined') {
|
|
536
|
+
playing = lastState.playing;
|
|
537
|
+
}
|
|
538
|
+
if (typeof (lastState.playerState) !== 'undefined') {
|
|
539
|
+
if (typeof (lastState.playerState.id) !== 'undefined') {
|
|
540
|
+
id = lastState.playerState.id;
|
|
535
541
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
return messageConstructor('command', {'payload': '
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// speaker
|
|
546
|
+
if ("TargetMediaState" in message.payload) {
|
|
547
|
+
let TargetMediaState = message.payload.TargetMediaState;
|
|
548
|
+
if (id) {
|
|
549
|
+
return messageConstructor('command', { 'payload': (TargetMediaState ? 'stop' : 'play') })
|
|
550
|
+
} else if (!id && !TargetMediaState && noTrackPhrase) {
|
|
551
|
+
return messageConstructor('voice', { 'payload': noTrackPhrase })
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// tv
|
|
556
|
+
if ("Active" in message.payload) {
|
|
557
|
+
let Active = message.payload.Active;
|
|
558
|
+
if (id) {
|
|
559
|
+
return messageConstructor('command', { 'payload': (Active ? 'play' : 'stop') })
|
|
560
|
+
} else if (!id && Active && noTrackPhrase) {
|
|
561
|
+
return messageConstructor('voice', { 'payload': noTrackPhrase })
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// tv + RemoteKey
|
|
566
|
+
if ("RemoteKey" in message.payload) {
|
|
567
|
+
let RemoteKey = message.payload.RemoteKey;
|
|
568
|
+
switch (RemoteKey) {
|
|
569
|
+
case '7':
|
|
570
|
+
return messageConstructor('command', { 'payload': 'forward' }, device)
|
|
571
|
+
case '6':
|
|
572
|
+
return messageConstructor('command', { 'payload': 'backward' }, device)
|
|
573
|
+
case '4':
|
|
574
|
+
return messageConstructor('command', { 'payload': 'next' })
|
|
575
|
+
case '5':
|
|
576
|
+
return messageConstructor('command', { 'payload': 'prev' })
|
|
577
|
+
case '11':
|
|
578
|
+
if (playing) {
|
|
579
|
+
return messageConstructor('command', { 'payload': 'stop' })
|
|
548
580
|
} else {
|
|
549
|
-
|
|
581
|
+
if (id) {
|
|
582
|
+
return messageConstructor('command', { 'payload': 'play' })
|
|
583
|
+
} else if (!id && noTrackPhrase) {
|
|
584
|
+
return messageConstructor('voice', { 'payload': noTrackPhrase })
|
|
585
|
+
}
|
|
550
586
|
}
|
|
551
|
-
|
|
552
|
-
case '{"VolumeSelector":1}':
|
|
553
|
-
return messageConstructor('command', {'payload': 'volumedown'}, device)
|
|
554
|
-
case '{"VolumeSelector":0}':
|
|
555
|
-
return messageConstructor('command', {'payload': 'volumeup'}, device)
|
|
556
|
-
default:
|
|
557
|
-
debugMessage('unknown command')
|
|
558
|
-
return messageConstructor('command', {'payload': 'ping'})
|
|
587
|
+
}
|
|
559
588
|
}
|
|
560
|
-
|
|
589
|
+
|
|
590
|
+
// tv + VolumeSelector
|
|
591
|
+
if ("VolumeSelector" in message.payload) {
|
|
592
|
+
let VolumeSelector = message.payload.VolumeSelector;
|
|
593
|
+
return messageConstructor('command', { 'payload': (VolumeSelector ? 'volumedown' : 'volumeup') }, device)
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
debugMessage('unknown command')
|
|
597
|
+
return messageConstructor('command', { 'payload': 'ping' })
|
|
561
598
|
} else {
|
|
562
|
-
return messageConstructor('command', {'payload': 'ping'})
|
|
599
|
+
return messageConstructor('command', { 'payload': 'ping' })
|
|
563
600
|
}
|
|
564
|
-
case 'raw':
|
|
601
|
+
case 'raw':
|
|
565
602
|
if (Array.isArray(message.payload)) { return message.payload }
|
|
566
603
|
return [message.payload];
|
|
567
|
-
case 'stopListening':
|
|
604
|
+
case 'stopListening':
|
|
568
605
|
return [{
|
|
569
606
|
"command": "serverAction",
|
|
570
607
|
"serverActionEventPayload": {
|
|
@@ -576,7 +613,7 @@ module.exports = function(RED) {
|
|
|
576
613
|
|
|
577
614
|
}
|
|
578
615
|
function sendMessage(deviceId, messageType, message) {
|
|
579
|
-
|
|
616
|
+
|
|
580
617
|
try {
|
|
581
618
|
let device = searchDeviceByID(deviceId);
|
|
582
619
|
//debugMessage(`deviceId: ${searchDeviceByID(deviceId)}`);
|
|
@@ -617,19 +654,19 @@ module.exports = function(RED) {
|
|
|
617
654
|
//debugMessage(`timeCur: ${timeCurrent} timeMin: ${timeMin} timeMax: ${timeMax}`);
|
|
618
655
|
return [false, daySheduler.phrase];
|
|
619
656
|
}
|
|
620
|
-
|
|
657
|
+
|
|
621
658
|
|
|
622
659
|
}
|
|
623
|
-
|
|
660
|
+
|
|
624
661
|
function searchDeviceByID(id) {
|
|
625
662
|
if (node.deviceList) {
|
|
626
663
|
return node.deviceList.find(device => device.id == id)
|
|
627
|
-
}
|
|
664
|
+
}
|
|
628
665
|
}
|
|
629
666
|
function onPing(device) {
|
|
630
667
|
if (device) {sendMessage(device.id, 'command', {payload: 'ping'});}
|
|
631
668
|
}
|
|
632
|
-
|
|
669
|
+
|
|
633
670
|
function onPing(device) {
|
|
634
671
|
sendMessage(device.id, 'command', {payload: 'ping'});
|
|
635
672
|
}
|
|
@@ -638,19 +675,19 @@ module.exports = function(RED) {
|
|
|
638
675
|
if (device) {
|
|
639
676
|
if (device.ws) {
|
|
640
677
|
switch(device.ws.readyState){
|
|
641
|
-
case 0:
|
|
678
|
+
case 0:
|
|
642
679
|
return {"color": "yellow", "text": "connecting..."}
|
|
643
|
-
case 1:
|
|
680
|
+
case 1:
|
|
644
681
|
return {"color": "green", "text": "connected"}
|
|
645
|
-
case 2:
|
|
682
|
+
case 2:
|
|
646
683
|
return {"color": "red", "text": "disconnecting"}
|
|
647
|
-
case 3:
|
|
684
|
+
case 3:
|
|
648
685
|
return {"color": "red", "text": "disconnected"}
|
|
649
686
|
default:
|
|
650
687
|
return {"color": "red", "text": "disconnected"}
|
|
651
688
|
}
|
|
652
|
-
|
|
653
|
-
}
|
|
689
|
+
|
|
690
|
+
}
|
|
654
691
|
} else {
|
|
655
692
|
return {"color": "red", "text": "disconnected"}
|
|
656
693
|
}
|
|
@@ -658,7 +695,7 @@ module.exports = function(RED) {
|
|
|
658
695
|
}
|
|
659
696
|
|
|
660
697
|
|
|
661
|
-
|
|
698
|
+
|
|
662
699
|
function registerDevice(deviceId, nodeId, parameters) {
|
|
663
700
|
let device = searchDeviceByID(deviceId);
|
|
664
701
|
debugMessage(`Recieved parameters ${JSON.stringify(parameters)} for station id ${deviceId}`);
|
|
@@ -670,7 +707,7 @@ module.exports = function(RED) {
|
|
|
670
707
|
debugMessage(`Device ${device.id} already registered with manager id ${device.manager}. Updating parameters and restart...`);
|
|
671
708
|
reconnect(device);
|
|
672
709
|
return 1;
|
|
673
|
-
|
|
710
|
+
|
|
674
711
|
}
|
|
675
712
|
//новый и первый запрос на регистрацию для устройства
|
|
676
713
|
if (typeof(device.manager) == 'undefined') {
|
|
@@ -699,7 +736,7 @@ module.exports = function(RED) {
|
|
|
699
736
|
registrationBuffer.splice(registrationBuffer.indexOf(currentBuffer), 1)
|
|
700
737
|
debugMessage(`Element from registration buffer was deleted. Current buffer size is ${registrationBuffer.length}`)
|
|
701
738
|
}
|
|
702
|
-
reconnect(device);
|
|
739
|
+
reconnect(device);
|
|
703
740
|
return 0;
|
|
704
741
|
}
|
|
705
742
|
//новый запрос на регистрацию при наличии уже зарегистрированной ноды
|
|
@@ -717,9 +754,9 @@ module.exports = function(RED) {
|
|
|
717
754
|
registrationBuffer.push({"id": deviceId, "manager": nodeId, "parameters": parameters});
|
|
718
755
|
debugMessage(`New element in registration buffer. Current buffer size is ${registrationBuffer.length}`)
|
|
719
756
|
}
|
|
720
|
-
|
|
757
|
+
|
|
721
758
|
}
|
|
722
|
-
|
|
759
|
+
|
|
723
760
|
|
|
724
761
|
}
|
|
725
762
|
function unregisterDevice(deviceId, nodeId){
|
|
@@ -730,7 +767,7 @@ module.exports = function(RED) {
|
|
|
730
767
|
device.parameters = {};
|
|
731
768
|
debugMessage(`For device ${deviceId} was succesfully unregistred managment node whith id ${device.manager}`);
|
|
732
769
|
debugMessage(`device is: ${device}`);
|
|
733
|
-
return 0;
|
|
770
|
+
return 0;
|
|
734
771
|
} else {
|
|
735
772
|
return 2;
|
|
736
773
|
}
|
|
@@ -751,7 +788,7 @@ module.exports = function(RED) {
|
|
|
751
788
|
if (device.savedVolumeLevel) { sendMessage(device.id, 'raw',{payload: {
|
|
752
789
|
"command": "setVolume",
|
|
753
790
|
"volume": parseFloat(device.savedVolumeLevel)
|
|
754
|
-
}
|
|
791
|
+
}
|
|
755
792
|
});
|
|
756
793
|
}
|
|
757
794
|
device.waitForIdle = false;
|
|
@@ -760,13 +797,13 @@ module.exports = function(RED) {
|
|
|
760
797
|
function onStopPlay(device, phrase) {
|
|
761
798
|
sendMessage(device.id, 'command', {payload: 'stop'});
|
|
762
799
|
if (phrase.length > 0 && device.lastState.aliceState != 'SPEAKING') {sendMessage(device.id, 'tts', {payload: phrase, stopListening: true});}
|
|
763
|
-
}
|
|
800
|
+
}
|
|
764
801
|
function onClose() {
|
|
765
802
|
clearInterval(node.interval);
|
|
766
803
|
node.deviceList = [];
|
|
767
804
|
node.removeListener('deviceReady', onDeviceReady)
|
|
768
805
|
}
|
|
769
|
-
|
|
806
|
+
|
|
770
807
|
node.on('refreshHttp', function(activeList, readyList) {
|
|
771
808
|
RED.httpAdmin.get("/yandexdevices_"+node.id, RED.auth.needsPermission('yandex-login.read'), function(req,res) {
|
|
772
809
|
res.json({"devices": readyList});
|
|
@@ -775,8 +812,8 @@ module.exports = function(RED) {
|
|
|
775
812
|
res.json({"devices": activeList});
|
|
776
813
|
});
|
|
777
814
|
});
|
|
778
|
-
|
|
779
|
-
node.on('refreshHttpDNS', function(dnsList) {
|
|
815
|
+
|
|
816
|
+
node.on('refreshHttpDNS', function(dnsList) {
|
|
780
817
|
RED.httpAdmin.get("/mdns/"+node.id, RED.auth.needsPermission('yandex-login.read'), function(req,res) {
|
|
781
818
|
res.json({"SearchResult": dnsList});
|
|
782
819
|
});
|
|
@@ -800,7 +837,7 @@ module.exports = function(RED) {
|
|
|
800
837
|
res.json({"error": 'no device found'});
|
|
801
838
|
}
|
|
802
839
|
});
|
|
803
|
-
|
|
840
|
+
|
|
804
841
|
// main init
|
|
805
842
|
if (typeof(node.token) !== 'undefined') {
|
|
806
843
|
debugMessage(`Starting server with id ${node.id}`)
|