node-red-contrib-yandex-station-management 0.3.7 → 0.3.9
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/README.md +89 -12
- package/examples/Dashboard-player-twocolors.json +45 -0
- package/nodes/yandex-login.js +9 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
- Яндекс Станция мини 2 с экраном(протестировано)
|
|
6
6
|
- Яндекс Станция лайт(протестировано)
|
|
7
7
|
- Яндекс Станци Макс(протестировано)
|
|
8
|
-
- Яндекс Модуль(
|
|
9
|
-
- Яндекс Модуль - 2 (
|
|
8
|
+
- Яндекс Модуль( протестировано)
|
|
9
|
+
- Яндекс Модуль - 2 (протестировано)
|
|
10
10
|
- JBL Link Music(не протестировано)
|
|
11
11
|
- JBL Link Portable(протестировано)
|
|
12
12
|
|
|
@@ -16,11 +16,13 @@
|
|
|
16
16
|
- находятся в одной локальной сети с сервером Node-Red
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
Для работы требуется токен от Яндекс.Музыки.
|
|
19
|
+
Для работы требуется токен от Яндекс.Музыки.
|
|
20
20
|
В модуле в экспериментальном режиме реализована возможность получения токена из логин-пароля(Спасибо слать [сюда](https://github.com/twocolors)). Если получение токена не отрабатывает, то стоит попробовать включить и отключить двух-факторную аутентификацию в настройках Яндекса. [Источник](https://github.com/AlexxIT/YandexStation/issues/103). Убедиться в безопасности использования учетных данных можно, посмотрев [код](./nodes/yandex-login.html)
|
|
21
21
|
|
|
22
22
|
Второй из варинатов его получения описан в [FAQ](#faq)
|
|
23
23
|
|
|
24
|
+
Третий из вариантов получения токена описан [тут](https://github.com/MarshalX/yandex-music-api/discussions/513#discussioncomment-2729781)
|
|
25
|
+
|
|
24
26
|
Возможна работа с несколькими устройствами(протестировано) и несколькими учетными записями(протестировано).
|
|
25
27
|
|
|
26
28
|
Состоит из 4 нод, позволяющих гибко настраивать автоматизации и использовать голосовые уведомления:
|
|
@@ -39,7 +41,7 @@
|
|
|
39
41
|
## Первоначальная настройка.
|
|
40
42
|
После установки для начала работы добавить любую ноду, ввести учетные данные(токен) в раздел Login, сохранить и нажать Deploy(обязательно!). Как получить токен - написано в FAQ.
|
|
41
43
|
|
|
42
|
-
После деплоя в настройках ноды в поле Station должны появиться станции доступные для управления.
|
|
44
|
+
После деплоя в настройках ноды в поле Station должны появиться станции доступные для управления.
|
|
43
45
|
|
|
44
46
|
Если станция не появилась в списке, то можно подождать пару минут или перезапустить Node-Red.
|
|
45
47
|
|
|
@@ -114,7 +116,7 @@ Phrase to say - фраза, которую скажет Алиса вместо
|
|
|
114
116
|
Отправка команды, вместо того, чтобы говорить ее колонке голосом: "Включи свет", "Включи музыку", "Включи мой плейлист", "Отключись через 15 минут" и так далее.
|
|
115
117
|
|
|
116
118
|
#### TTS.
|
|
117
|
-
Воспроизведение голосом отправленных фраз - Text to Speech. Не имеет ограничения по символам.
|
|
119
|
+
Воспроизведение голосом отправленных фраз - Text to Speech. Не имеет ограничения по символам.
|
|
118
120
|
Параметры для TTS могут задаваться как в настройках, так и некоторые из них могут быть переопределены входящим сообщением.
|
|
119
121
|
- Text. Откуда и какой текст проговорить.
|
|
120
122
|
- из входящего сообщения (msg.payload по-умолчанию)
|
|
@@ -123,7 +125,7 @@ Phrase to say - фраза, которую скажет Алиса вместо
|
|
|
123
125
|
- JSON: выбрать сообщение случайным образом из массива вида ["один", "два", "три"].
|
|
124
126
|
- Voice. Выбор голоса для генерации. Может быть переопределено через msg.voice
|
|
125
127
|
- Effect. Эффекты для генерации голоса. Может быть переопределено через msg.effect
|
|
126
|
-
-
|
|
128
|
+
-
|
|
127
129
|
Есть ряд опций:
|
|
128
130
|
- Volume. Позволяет произносить фразу заданной громкостью. Если не выбрано, то фраза произносится с текущим уровнем громкости. После произнесения уровень громкости вернется в изначальный. Может быть переопределено через msg.volume
|
|
129
131
|
- Whisper. Позволяет произнести фразу шептом.. Переопределяется через msg.whisper
|
|
@@ -236,7 +238,36 @@ Phrase to say - фраза, которую скажет Алиса вместо
|
|
|
236
238
|
"volume" : 0.2
|
|
237
239
|
}
|
|
238
240
|
```
|
|
239
|
-
10.
|
|
241
|
+
10. Включить радио
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"command": "playRadio",
|
|
245
|
+
"id": "detskoe"
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
11. Режим повтора. "One"/"All"/"None"
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"command": "repeat",
|
|
252
|
+
"mode": "One"
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
12. Режим вразброс. Срабатывает, когда есть очередь треков(включен плейоист, альбом, артист) true/false
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"command": "shuffle",
|
|
259
|
+
"enable": true
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
13. Принудительно включить режим индикации Алисы - занято, слушаю, простой "LISTENING"/"BUSY"/"IDLE"
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"command": "showAliceVisualState",
|
|
266
|
+
"aliceStateName": "LISTENING",
|
|
267
|
+
"recognizedPhrase": ""
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
14. Отправить "Текст" для TTS.
|
|
240
271
|
Больше не работает!
|
|
241
272
|
```json
|
|
242
273
|
{
|
|
@@ -244,14 +275,14 @@ Phrase to say - фраза, которую скажет Алиса вместо
|
|
|
244
275
|
"text" : "Повторяй за мной 'Текст'"
|
|
245
276
|
}
|
|
246
277
|
```
|
|
247
|
-
|
|
278
|
+
15. Отправить голосовую команду.
|
|
248
279
|
```json
|
|
249
280
|
{
|
|
250
281
|
"command" : "sendText",
|
|
251
282
|
"text" : "Включи музыку"
|
|
252
283
|
}
|
|
253
284
|
```
|
|
254
|
-
|
|
285
|
+
16. Прервать "слушание" после TTS и не только:
|
|
255
286
|
```json
|
|
256
287
|
{
|
|
257
288
|
"command": "serverAction",
|
|
@@ -262,7 +293,7 @@ Phrase to say - фраза, которую скажет Алиса вместо
|
|
|
262
293
|
}
|
|
263
294
|
```
|
|
264
295
|
|
|
265
|
-
|
|
296
|
+
17. Отправить "Текст" для TTS со спецэффектами (**raw режим**):
|
|
266
297
|
```json
|
|
267
298
|
{
|
|
268
299
|
"command": "serverAction",
|
|
@@ -314,7 +345,53 @@ Phrase to say - фраза, которую скажет Алиса вместо
|
|
|
314
345
|
```json
|
|
315
346
|
"value": "<speaker voice='kostya' audio='alice-sounds-game-win-1.opus' effect='megaphone'>добро пожаловать"
|
|
316
347
|
```
|
|
317
|
-
|
|
348
|
+
18. Приветствие как в автомобиле. Кратко скажет погоду и пробки
|
|
349
|
+
```json
|
|
350
|
+
{
|
|
351
|
+
"command": "serverAction",
|
|
352
|
+
"serverActionEventPayload": {
|
|
353
|
+
"type": "server_action",
|
|
354
|
+
"name": "update_form",
|
|
355
|
+
"payload": {
|
|
356
|
+
"form_update": {
|
|
357
|
+
"name": "personal_assistant.automotive.greeting"
|
|
358
|
+
},
|
|
359
|
+
"resubmit": true
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
19. Включить и выключить блютуз
|
|
365
|
+
```json
|
|
366
|
+
{
|
|
367
|
+
"command": "serverAction",
|
|
368
|
+
"serverActionEventPayload": {
|
|
369
|
+
"type": "server_action",
|
|
370
|
+
"name": "update_form",
|
|
371
|
+
"payload": {
|
|
372
|
+
"form_update": {
|
|
373
|
+
"name": "personal_assistant.scenarios.bluetooth_on"
|
|
374
|
+
},
|
|
375
|
+
"resubmit": true
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
```json
|
|
381
|
+
{
|
|
382
|
+
"command": "serverAction",
|
|
383
|
+
"serverActionEventPayload": {
|
|
384
|
+
"type": "server_action",
|
|
385
|
+
"name": "update_form",
|
|
386
|
+
"payload": {
|
|
387
|
+
"form_update": {
|
|
388
|
+
"name": "personal_assistant.scenarios.bluetooth_off"
|
|
389
|
+
},
|
|
390
|
+
"resubmit": true
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
318
395
|
|
|
319
396
|
#### Stop listening.
|
|
320
397
|
|
|
@@ -370,7 +447,7 @@ Phrase to say - фраза, которую скажет Алиса вместо
|
|
|
370
447
|

|
|
371
448
|

|
|
372
449
|
|
|
373
|
-
Есть еще один вариант от
|
|
450
|
+
Есть еще один вариант от [@twocolors](https://github.com/twocolors), в примерах.
|
|
374
451
|
|
|
375
452
|
Добавляется простым flow и выглядит отлично)
|
|
376
453
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "db7d25d3.9110a8",
|
|
4
|
+
"type": "ui_template",
|
|
5
|
+
"z": "0c069b6a05278757",
|
|
6
|
+
"group": "8865f91.bf77188",
|
|
7
|
+
"name": "",
|
|
8
|
+
"order": 1,
|
|
9
|
+
"width": "8",
|
|
10
|
+
"height": "8",
|
|
11
|
+
"format": "<style>\n .yandex-player {\n height: 100%;\n position: relative;\n min-height: 128px;\n z-index: 1;\n background: url(\"//avatars.mds.yandex.net/get-music-misc/29541/img.5e6a1c5b38be6e3bae26558a/600x600\");\n background-repeat: no-repeat;\n background-position: center;\n background-size: cover;\n }\n\n .yandex-player .gradient {\n background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.75));\n position: absolute;\n bottom: 0;\n left: 0;\n }\n\n .yandex-player .controls {\n margin: 24px 32px;\n color: rgba(255, 255, 255, 0.75);\n font-family: Roboto, Noto, sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-weight: lighter;\n }\n\n .yandex-player .title {\n font-size: larger;\n }\n\n .yandex-player .subtitle {\n font-size: medium;\n }\n\n .yandex-player .ui {\n padding: 16px 0 24px 0;\n }\n\n .yandex-player .ui i {\n cursor: pointer;\n }\n\n .yandex-player .ui i:hover {\n color: rgba(255, 255, 255, 1);\n }\n\n .yandex-player .ui-btn {\n float: left;\n display: flex;\n justify-content: space-between;\n width: 20%;\n }\n\n .yandex-player .ui-volume {\n float: right;\n display: flex;\n justify-content: space-between;\n width: 80%;\n vertical-align: middle;\n }\n\n .yandex-player .slider {\n position: relative;\n cursor: pointer;\n width: 100%;\n margin: 0 16px;\n opacity: 1;\n display: inline-block;\n }\n\n .yandex-player .slider_base {\n width: 100%;\n height: 2px;\n background-color: rgba(255, 255, 255, 0.75);\n border-radius: 2px;\n position: absolute;\n top: 8px;\n }\n\n .yandex-player .slider_progress {\n height: 4px;\n background-color: rgba(255, 255, 255, 1);\n border-radius: 2px;\n position: absolute;\n top: 7px;\n }\n\n .yandex-player .bar {\n position: absolute;\n cursor: pointer;\n width: 100%;\n opacity: 1;\n bottom: 0;\n height: 6px;\n background-color: rgba(255, 255, 255, 0.75);\n }\n\n .yandex-player .bar_progress {\n width: 0;\n height: 100%;\n background-color: #fc0;\n }\n\n .yandex-player .red {\n color: red;\n }\n</style>\n<div class=\"yandex-player\">\n <div class=\"gradient\" style=\"min-height: 128px; width: 100%;\">\n <div class=\"controls\">\n <div class=\"title\"></div>\n <div class=\"subtitle\"></div>\n <div class=\"ui\">\n <div class=\"ui-btn\">\n <i class=\"fa fa-step-backward fa-lg\" aria-hidden=\"true\"></i>\n <i class=\"btn-play-pause fa fa-play fa-lg\" aria-hidden=\"true\"></i>\n <i class=\"fa fa-step-forward fa-lg\" aria-hidden=\"true\"></i>\n </div>\n\n <div class=\"ui-volume\">\n <div class=\"slider\">\n <div class=\"slider_base\"></div>\n <div class=\"slider_progress\"></div>\n </div>\n <div style=\"width: 10%;\">\n <i class=\"btn-volume fa fa-volume-up fa-lg\" aria-hidden=\"true\"></i>\n </div>\n <div style=\"width: 10%; text-align: right; margin-left: 16px;\">\n <i class=\"fa fa-heart fa-lg\" aria-hidden=\"true\"></i>\n </div>\n </div>\n </div>\n </div>\n <div class=\"bar\">\n <div class=\"bar_progress\"></div>\n </div>\n </div>\n</div>\n<script>\n /*! js-cookie v3.0.0-rc.1 | MIT */\n !function (e, t) { \"object\" == typeof exports && \"undefined\" != typeof module ? module.exports = t() : \"function\" == typeof define && define.amd ? define(t) : (e = e || self, function () { var n = e.Cookies, r = e.Cookies = t(); r.noConflict = function () { return e.Cookies = n, r } }()) }(this, function () { \"use strict\"; function e(e) { for (var t = 1; t < arguments.length; t++) { var n = arguments[t]; for (var r in n) e[r] = n[r] } return e } var t = { read: function (e) { return e.replace(/(%[\\dA-F]{2})+/gi, decodeURIComponent) }, write: function (e) { return encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g, decodeURIComponent) } }; return function n(r, o) { function i(t, n, i) { if (\"undefined\" != typeof document) { \"number\" == typeof (i = e({}, o, i)).expires && (i.expires = new Date(Date.now() + 864e5 * i.expires)), i.expires && (i.expires = i.expires.toUTCString()), t = encodeURIComponent(t).replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent).replace(/[()]/g, escape), n = r.write(n, t); var c = \"\"; for (var u in i) i[u] && (c += \"; \" + u, !0 !== i[u] && (c += \"=\" + i[u].split(\";\")[0])); return document.cookie = t + \"=\" + n + c } } return Object.create({ set: i, get: function (e) { if (\"undefined\" != typeof document && (!arguments.length || e)) { for (var n = document.cookie ? document.cookie.split(\"; \") : [], o = {}, i = 0; i < n.length; i++) { var c = n[i].split(\"=\"), u = c.slice(1).join(\"=\"); '\"' === u[0] && (u = u.slice(1, -1)); try { var f = t.read(c[0]); if (o[f] = r.read(u, f), e === f) break } catch (e) { } } return e ? o[e] : o } }, remove: function (t, n) { i(t, \"\", e({}, n, { expires: -1 })) }, withAttributes: function (t) { return n(this.converter, e({}, this.attributes, t)) }, withConverter: function (t) { return n(e({}, this.converter, t), this.attributes) } }, { attributes: { value: Object.freeze(o) }, converter: { value: Object.freeze(r) } }) }(t, { path: \"/\" }) });\n</script>\n<script>\n (function (scope) {\n // function\n function setVolume(volume) {\n //console.log('setVolume:' + volume);\n\n if (volume <= 4.5) {\n volume = 0;\n } else if (volume > 4.5 && volume < 10) {\n volume = 10;\n } else if (volume >= 95.5) {\n volume = 100;\n }\n\n let toggle = volume > 0 ? true : false;\n $('.btn-volume').toggleClass('fa-volume-up', toggle).toggleClass('fa-volume-off', !toggle);\n\n scope.send({ 'payload': { 'command': 'setVolume', 'volume': volume / 100 } });\n }\n\n function setProgress(playerState) {\n let progress = (playerState.progress / playerState.duration) * 100;\n //$('.bar_progress').animate({ width: progress + '%' }, 'fast');\n $('.bar_progress').width(progress + '%');\n }\n\n function setLike(id) {\n let like = JSON.parse(Cookies.get('yandex-like') || '{}');\n like[id] = id;\n Cookies.set('yandex-like', JSON.stringify(like), { expires: 3 });\n $('.fa-heart').addClass('red');\n\n scope.send({ payload: { 'command': 'sendText', 'text': 'поставь лайк' } });\n }\n\n function setDislike(id) {\n let like = JSON.parse(Cookies.get('yandex-like') || '{}');\n if (like[id] !== undefined) {\n delete like[id];\n }\n Cookies.set('yandex-like', JSON.stringify(like), { expires: 3 });\n $('.fa-heart').removeClass('red');\n\n scope.send({ payload: { 'command': 'sendText', 'text': 'поставь дизлайк' } });\n }\n\n // init+update\n scope.$watch('msg', function (msg) {\n if (msg) {\n\n if ('volume' in msg.payload) {\n $('.slider_progress').animate({width: msg.payload.volume * 100 + '%'}, 150);\n }\n\n if (msg.payload.playing === true || msg.payload.playing === false) {\n $('.btn-play-pause').toggleClass('fa-pause', msg.payload.playing).toggleClass('fa-play', !msg.payload.playing);\n }\n\n if ('playerState' in msg.payload) {\n //update title\n $('.title').text(msg.payload.playerState.title);\n $('.subtitle').text(msg.payload.playerState.subtitle);\n\n //update img\n let thumb = 'avatars.mds.yandex.net/get-music-misc/29541/img.5e6a1c5b38be6e3bae26558a/%%';\n if (msg.payload.playerState.extra !== null && typeof msg.payload.playerState.extra.coverURI !== \"undefined\") {\n thumb = msg.payload.playerState.extra.coverURI;\n }\n thumb = '//' + thumb.replace(/%%/g, \"600x600\");\n\n if (thumb != $('.yandex-player').attr('data-image')) {\n $('.yandex-player').css('background-image', 'url(' + thumb + ')').attr('data-image', thumb);\n }\n\n setProgress(msg.payload.playerState);\n\n let id = msg.payload.playerState.id;\n if (id != $('.fa-heart').attr('data-id')) {\n $('.fa-heart').removeClass('red').attr('data-id', id);\n let like = JSON.parse(Cookies.get('yandex-like') || '{}');\n if (like[id]) {\n $('.fa-heart').addClass('red');\n }\n }\n }\n }\n });\n\n // controls\n $('.btn-play-pause').click(function () {\n scope.send({ 'payload': { 'command': scope.msg.payload.playing ? 'stop' : 'play' } });\n });\n\n $('.fa-step-backward').click(function () {\n scope.send({ 'payload': { 'command': 'prev' } });\n });\n\n $('.fa-step-forward').click(function () {\n scope.send({ 'payload': { 'command': 'next' } });\n });\n\n // slider volume\n $('.slider').on('click', function (event) {\n let left = $('.slider').offset().left;\n let width = $('.slider').outerWidth();\n\n let volume = (event.clientX - left) / width * 100;\n\n setVolume(volume);\n });\n\n // volume\n $('.btn-volume').on('click', function () {\n if ($(this).hasClass('fa-volume-up')) {\n Cookies.set('yandex-volume-save', $('.slider_progress').width(), { expires: 7 });\n setVolume(0);\n } else if ($(this).hasClass('fa-volume-off')) {\n setVolume(Cookies.get('yandex-volume-save'));\n }\n });\n\n // bar\n $('.bar').on('click', function (event) {\n let left = $('.bar').offset().left;\n let width = $('.bar').outerWidth();\n\n let percent = (event.clientX - left) / width * 100;\n let position = Math.round(percent * (scope.msg.payload.playerState.duration / 100));\n\n scope.send({ 'payload': { 'command': 'rewind', 'position': position } });\n if ($('.btn-play-pause').hasClass('fa-play')) {\n scope.send({ 'payload': { 'command': 'play' } });\n }\n });\n\n //like\n $('.fa-heart').on('click', function () {\n let id = $(this).attr('data-id');\n if (!$(this).hasClass('red')) {\n setLike(id);\n } else {\n setDislike(id);\n }\n });\n\n })(scope);\n</script>",
|
|
12
|
+
"storeOutMessages": true,
|
|
13
|
+
"fwdInMessages": false,
|
|
14
|
+
"resendOnRefresh": true,
|
|
15
|
+
"templateScope": "local",
|
|
16
|
+
"className": "",
|
|
17
|
+
"x": 400,
|
|
18
|
+
"y": 260,
|
|
19
|
+
"wires": [
|
|
20
|
+
[
|
|
21
|
+
"ae2a51d2fe666ff9"
|
|
22
|
+
]
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "8865f91.bf77188",
|
|
27
|
+
"type": "ui_group",
|
|
28
|
+
"name": "Alice",
|
|
29
|
+
"tab": "bd17abae.b2461",
|
|
30
|
+
"order": 1,
|
|
31
|
+
"disp": true,
|
|
32
|
+
"width": "8",
|
|
33
|
+
"collapse": false,
|
|
34
|
+
"className": ""
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "bd17abae.b2461",
|
|
38
|
+
"type": "ui_tab",
|
|
39
|
+
"name": "Кубик³",
|
|
40
|
+
"icon": "dashboard",
|
|
41
|
+
"order": 1,
|
|
42
|
+
"disabled": false,
|
|
43
|
+
"hidden": false
|
|
44
|
+
}
|
|
45
|
+
]
|
package/nodes/yandex-login.js
CHANGED
|
@@ -94,7 +94,8 @@ module.exports = function(RED) {
|
|
|
94
94
|
if (bufferStation) {
|
|
95
95
|
let result = registerDevice(bufferStation.id, bufferStation.manager, bufferStation.parameters)
|
|
96
96
|
if (result != 2 && result != undefined) {
|
|
97
|
-
|
|
97
|
+
//https://github.com/n0name45/node-red-contrib-yandex-station-management/issues/20#issuecomment-1373709408
|
|
98
|
+
//registrationBuffer.splice(registrationBuffer.indexOf(bufferStation,1));
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
}
|
|
@@ -294,7 +295,7 @@ module.exports = function(RED) {
|
|
|
294
295
|
device.watchDog = setTimeout(() => {
|
|
295
296
|
if (typeof(device) != 'undefined' && typeof(device.ws) != 'undefined') {device.ws.close()}
|
|
296
297
|
}, 10000);
|
|
297
|
-
device.pingInterval = setInterval(onPing,
|
|
298
|
+
device.pingInterval = setInterval(onPing,1500,device);
|
|
298
299
|
debugMessage(`${device.id}: Kill connection watchdog`);
|
|
299
300
|
clearTimeout(device.watchDogConn);
|
|
300
301
|
clearTimeout(device.timer);
|
|
@@ -394,7 +395,7 @@ module.exports = function(RED) {
|
|
|
394
395
|
};
|
|
395
396
|
|
|
396
397
|
function messageConstructor(messageType, message, device){
|
|
397
|
-
let commands = ['play', 'stop', 'next', 'prev', 'ping'];
|
|
398
|
+
let commands = ['play', 'stop', 'next', 'prev', 'ping', 'softwareVersion'];
|
|
398
399
|
let extraCommands = ['forward', 'backward', 'volumeup', 'volumedown', 'volume'];
|
|
399
400
|
switch(messageType){
|
|
400
401
|
case 'command':
|
|
@@ -454,7 +455,7 @@ module.exports = function(RED) {
|
|
|
454
455
|
} else {
|
|
455
456
|
debugMessage(`Bad command ${message.payload}`)
|
|
456
457
|
//node.error(`You can send commands in msg.payload from list as String ${commands + extraCommands}`);
|
|
457
|
-
return [{"command": "
|
|
458
|
+
return [{"command": "softwareVersion"}];
|
|
458
459
|
}
|
|
459
460
|
case 'voice':
|
|
460
461
|
debugMessage(`Message Voice command: ${message}`);
|
|
@@ -594,9 +595,9 @@ module.exports = function(RED) {
|
|
|
594
595
|
}
|
|
595
596
|
|
|
596
597
|
debugMessage('unknown command')
|
|
597
|
-
return messageConstructor('command', { 'payload': '
|
|
598
|
+
return messageConstructor('command', { 'payload': 'softwareVersion' })
|
|
598
599
|
} else {
|
|
599
|
-
return messageConstructor('command', { 'payload': '
|
|
600
|
+
return messageConstructor('command', { 'payload': 'softwareVersion' })
|
|
600
601
|
}
|
|
601
602
|
case 'raw':
|
|
602
603
|
if (Array.isArray(message.payload)) { return message.payload }
|
|
@@ -664,11 +665,11 @@ module.exports = function(RED) {
|
|
|
664
665
|
}
|
|
665
666
|
}
|
|
666
667
|
function onPing(device) {
|
|
667
|
-
if (device) {sendMessage(device.id, 'command', {payload: '
|
|
668
|
+
if (device) {sendMessage(device.id, 'command', {payload: 'softwareVersion'});}
|
|
668
669
|
}
|
|
669
670
|
|
|
670
671
|
function onPing(device) {
|
|
671
|
-
sendMessage(device.id, 'command', {payload: '
|
|
672
|
+
sendMessage(device.id, 'command', {payload: 'softwareVersion'});
|
|
672
673
|
}
|
|
673
674
|
function getStatus(id) {
|
|
674
675
|
let device = searchDeviceByID(id);
|