node-red-contrib-yandex-station-management 0.2.5 → 0.3.1

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 CHANGED
@@ -2,8 +2,10 @@
2
2
  С помощью модуля можно через локальное API управлять вопсроизведением на устройсвах Яндекса:
3
3
  - Яндекс Станция(протестировано)
4
4
  - Яндекс Станция мини(протестировано)
5
+ - Яндекс Станция мини 2 с экраном(протестировано)
5
6
  - Яндекс Станци Макс(протестировано)
6
7
  - Яндекс Модуль(не протестировано)
8
+ - Яндекс Модуль - 2 (в процессе тестирования)
7
9
  - JBL Link Music(не протестировано)
8
10
  - JBL Link Portable(протестировано)
9
11
 
@@ -13,7 +15,10 @@
13
15
  - находятся в одной локальной сети с сервером Node-Red
14
16
 
15
17
 
16
- Для работы требуется токен от Яндекс.Музыки. Один из варинатов его получения описан в [FAQ](#faq)
18
+ Для работы требуется токен от Яндекс.Музыки.
19
+ В модуле в экспериментальном режиме реализована возможность получения токена из логин-пароля(Спасибо слать [сюда](https://github.com/twocolors)). Если получение токена не отрабатывает, то стоит попробовать включить и отключить двух-факторную аутентификацию в настройках Яндекса. [Источник](https://github.com/AlexxIT/YandexStation/issues/103). Убедиться в безопасности использования учетных данных можно, посмотрев [код](./nodes/yandex-login.html)
20
+
21
+ Второй из варинатов его получения описан в [FAQ](#faq)
17
22
 
18
23
  Возможна работа с несколькими устройствами(протестировано) и несколькими учетными записями(протестировано).
19
24
 
@@ -33,7 +38,9 @@
33
38
  ## Первоначальная настройка.
34
39
  После установки для начала работы добавить любую ноду, ввести учетные данные(токен) в раздел Login, сохранить и нажать Deploy(обязательно!). Как получить токен - написано в FAQ.
35
40
 
36
- После деплоя в настройках ноды в поле Station должны появиться станции доступные для управления. Если станция не появилась в списке, то можно подождать пару минут или перезапустить Node-Red.
41
+ После деплоя в настройках ноды в поле Station должны появиться станции доступные для управления.
42
+
43
+ npmЕсли станция не появилась в списке, то можно подождать пару минут или перезапустить Node-Red.
37
44
 
38
45
  ## Описание возможностей и сценариев использования.
39
46
  ### Нода Station
@@ -106,13 +113,53 @@ Phrase to say - фраза, которую скажет Алиса вместо
106
113
  Отправка команды, вместо того, чтобы говорить ее колонке голосом: "Включи свет", "Включи музыку", "Включи мой плейлист", "Отключись через 15 минут" и так далее.
107
114
 
108
115
  #### TTS.
109
- Воспросизведение голосом отправленных фраз - Text to Speech. Не имеет ограничения по символам. Есть ряд опций:
110
- - Fixed volume level. Позволяет произносить фразу заданной громкостью. Если не выбрано, то фраза произносится с текущим уровнем громкости. После произнесения уровень громкости вернется в изначальный.
111
- - Prevent listening. Если выбрано, то колонка после воспроизведения не "слушает", что ей ответят.
112
- - Pause while TTS. Ставит воспроизведение плеера на паузу на время речи. Воспроизведение будет продолжено, только если что-то играло на момент поступления команды.
116
+ Воспроизведение голосом отправленных фраз - Text to Speech. Не имеет ограничения по символам.
117
+ Параметры для TTS могут задаваться как в настройках, так и некоторые из них могут быть переопределены входящим сообщением.
118
+ - Text. Откуда и какой текст проговорить.
119
+ - из входящего сообщения (msg.payload по-умолчанию)
120
+ - строго заданную строку
121
+ - переменную из flow или global context
122
+ - JSON: выбрать сообщение случайным образом из массива вида ["один", "два", "три"].
123
+ - Voice. Выбор голоса для генерации. Может быть переопределено через msg.voice
124
+ - Effect. Эффекты для генерации голоса. Может быть переопределено через msg.effect
125
+ -
126
+ Есть ряд опций:
127
+ - Volume. Позволяет произносить фразу заданной громкостью. Если не выбрано, то фраза произносится с текущим уровнем громкости. После произнесения уровень громкости вернется в изначальный. Может быть переопределено через msg.volume
128
+ - Prevent listening. Если выбрано, то колонка после воспроизведения не "слушает", что ей ответят. Может быть переопределено через msg.prevent_listening
129
+ - Pause while TTS. Ставит воспроизведение плеера на паузу на время речи. Воспроизведение будет продолжено, только если что-то играло на момент поступления команды. Может быть переопределено через msg.pause_music
113
130
  Все опции комбинируемы между собой.
114
131
 
115
- Работают голосовые [спецэффекты](https://yandex.ru/dev/dialogs/alice/doc/speech-effects-docpage/),[дополнительные голоса](https://yandex.ru/dev/dialogs/alice/doc/speech-effects-docpage/) и [звуки](https://yandex.ru/dev/dialogs/alice/doc/sounds-docpage/), при этом отправка производится в ввиде строки. Например, "смелость sil <[500]> город+а берёт"
132
+ ##### Добавление голосу жизни и красок.
133
+ ###### Раставляйте ударения
134
+ При необходимости ударные гласные в словах следует отмечать знаком «+», например:
135
+
136
+ остр+ота
137
+ м+ука
138
+
139
+ ##### Разделяйте слова
140
+ Длинные слова можно разбить на слова покороче и проставить ударения для каждого из этих коротких слов, например:
141
+
142
+ мн+ого пр+офильный
143
+ с+еми пал+атинск
144
+
145
+ ##### Меняйте написание слов
146
+ Некоторые слова можно попробовать писать так, как они слышатся:
147
+
148
+ «ненастный» — нен+асный
149
+ «пожалуйста» — пож+алуста
150
+
151
+ ###### Добавляйте паузы
152
+ Чтобы задать паузу между словами, используйте синтаксис sil <[ количество_миллисекунд ]>. Например:
153
+
154
+ смелость sil <[500]> город+а берёт
155
+
156
+ Каждый отделенный пробелами пунктуационный знак обозначается паузой в 50-100 мс.
157
+
158
+ ###### Добавляйте [звуки из библиотеки](https://yandex.ru/dev/dialogs/alice/doc/sounds-docpage/)
159
+
160
+ <speaker audio=\"alice-sounds-game-win-1.opus\"> У вас получилось!
161
+
162
+
116
163
 
117
164
 
118
165
  #### Homekit Formatted.
package/nodes/get.js CHANGED
@@ -10,36 +10,43 @@ module.exports = function(RED) {
10
10
  node.lastState = {};
11
11
  node.status({});
12
12
 
13
-
13
+
14
14
  function debugMessage(text){
15
15
  if (node.debugFlag) {
16
16
  node.log(text);
17
17
  }
18
18
  }
19
- function preparePayload(message){
20
- let payload = {};
19
+ function preparePayload(message,inputMsg){
20
+ //let payload = {};
21
21
  if (node.output == 'status') {
22
- payload = {'payload': message}
22
+ inputMsg.payload = message;
23
23
  } else if (node.output == 'homekit') {
24
24
  if (node.homekitFormat == 'speaker') {
25
- (message.playerState)? payload = {'payload': {
25
+ let ConfiguredName = `${(message.playerState.subtitle) ? message.playerState.subtitle : 'No Artist'} - ${(message.playerState.title) ? message.playerState.title : 'No Track Name'}`;
26
+ let title = `${message.playerState.title}`;
27
+ if (ConfiguredName.length > 64 && title.length > 0 && title.length <= 64) {
28
+ ConfiguredName = title;
29
+ } else {
30
+ ConfiguredName = title.substr(0, 61) + `...`;
31
+ }
32
+ (message.playerState)? inputMsg.payload = {
26
33
  "CurrentMediaState": (message.playing) ? 0 : 1,
27
- "ConfiguredName": `${(message.playerState.subtitle) ? message.playerState.subtitle : 'No Artist'} - ${(message.playerState.title) ? message.playerState.title : 'No Track Name'}`
28
- } }:payload = {'payload': {
34
+ "ConfiguredName": ConfiguredName
35
+ } :inputMsg.payload = {
29
36
  "CurrentMediaState": (message.playing) ? 0 : 1,
30
- "ConfiguredName": `No Artist - No Track`
31
- } }
37
+ "ConfiguredName": `No Artist - No Track Name`
38
+ }
32
39
  }else if (node.homekitFormat == 'tv') {
33
- payload = {'payload': {
40
+ inputMsg.payload = {
34
41
  "Active": (message.playing) ? 1 : 0
35
- }
36
- }
42
+ }
43
+
37
44
  }
38
45
  }
39
- return payload;
46
+ return inputMsg;
40
47
 
41
48
  }
42
-
49
+
43
50
 
44
51
  node.onStatus = function(data) {
45
52
  if (data) {
@@ -47,9 +54,9 @@ module.exports = function(RED) {
47
54
  //node.log('new status ' + data)
48
55
  }
49
56
  }
50
- node.onInput = function(){
51
- debugMessage('input message');
52
- ( 'aliceState' in node.lastState )?node.send(preparePayload(node.lastState)):node.send({'payload': {}})
57
+ node.onInput = function(msg, send, done){
58
+ debugMessage('current state: ' + JSON.stringify(node.lastState));
59
+ ( 'aliceState' in node.lastState )?node.send(preparePayload(node.lastState,msg)):node.send(msg)
53
60
  }
54
61
  node.onMessage = function(message){
55
62
  node.lastState = message;
@@ -57,7 +64,7 @@ module.exports = function(RED) {
57
64
  node.onClose = function(){
58
65
  node.controller.removeListener(`message_${node.station}`, node.onMessage)
59
66
  }
60
-
67
+
61
68
  node.on('input', node.onInput);
62
69
 
63
70
  node.on('close', node.onClose)
package/nodes/in.js CHANGED
@@ -10,9 +10,9 @@ module.exports = function(RED) {
10
10
  node.homekitFormat = config.homekitFormat;
11
11
  node.lastMessage = {};
12
12
  node.status({});
13
-
14
13
 
15
-
14
+
15
+
16
16
  debugMessage(`Node settings: ID: ${node.station}, Output Format: ${node.output}, HK: ${node.homekitFormat}`);
17
17
  function debugMessage(text){
18
18
  if (node.debugFlag) {
@@ -26,20 +26,32 @@ module.exports = function(RED) {
26
26
  if (node.output == 'status') {
27
27
  payload = {'payload': message}
28
28
  } else if (node.output == 'homekit') {
29
- if (node.homekitFormat == 'speaker') {
30
- (message.playerState)? payload = {'payload': {
31
- "CurrentMediaState": (message.playing) ? 0 : 1,
32
- "ConfiguredName": `${(message.playerState.subtitle) ? message.playerState.subtitle : 'No Artist'} - ${(message.playerState.title) ? message.playerState.title : 'No Track Name'}`
33
- } }:payload = {'payload': {
34
- "CurrentMediaState": (message.playing) ? 0 : 1,
35
- "ConfiguredName": `No Artists - No Track`
36
- } }
37
- }else if (node.homekitFormat == 'tv') {
38
- payload = {'payload': {
39
- "Active": (message.playing) ? 1 : 0
40
- }
41
- }
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)
42
53
  }
54
+
43
55
  }
44
56
  return payload;
45
57
 
@@ -50,8 +62,8 @@ module.exports = function(RED) {
50
62
  if ((JSON.stringify(node.lastMessage.payload) != JSON.stringify(message.payload))) {
51
63
  node.send(message)
52
64
  node.lastMessage = message
53
- debugMessage(`Sended message to HK: ${JSON.stringify(message)}`);
54
-
65
+ debugMessage(`Sended message to HK: ${JSON.stringify(message)}`);
66
+
55
67
  }
56
68
  } else {
57
69
  node.send(message);
@@ -77,7 +89,7 @@ module.exports = function(RED) {
77
89
  node.controller.on(`message_${node.station}`, node.onMessage);
78
90
  node.controller.on(`statusUpdate_${node.station}`, node.onStatus);
79
91
  }
80
-
92
+
81
93
  node.on('close', node.onClose);
82
94
 
83
95