Таймеры, несколько команд устройству, TTS (NodeJS)
Содержание
И вдумчивое отношение к инструкции
Мы не несём никакой ответственности за правильное или неправильное применение, или неприменение, и/или неспособность применить данное руководство.
Телефон вызова экстренных служб - 112.
13.10.2020 Появилась частичная поддержка таймеров "из коробки". Яндекс реализовал таймеры для включения/выключения устройств.
Соответствующая новость была размещена в телеграм канале "Около Яндекс.станции". Таймеры для сценариев пока отсутствуют и нет поддержки расписания.
21.01.2021 Появилась поддержка таймеров для сценариев и расписания для них же.
Соответствующая новость была размещена в телеграм канале "Около Яндекс.станции".
Постановка задачи
По мотивам статьи Алиса, таймеры, TTS и Node-RED, идея работы навыка взята здесь.
Необходима возможность выполнения нескольких сценариев за одну команду для Алисы, а так же возможность указать время выполнения сценария (таймер), не требуя Node-RED.
Выполнять составные команды. Примеры:
- Включи люстру и телевизор
- Выключи люстру и включи телевизор
- Включи аквариум и телевизор и выключи люстру
Выполнять сценарии в определенное время. Примеры:
- Добавить таймер включи ночную громкость в 23 часа
- Добавить таймер включи утреннюю громкость в 8 часов
- Выключи телевизор через 15 минут
Нам понадобятся:
- Сервер/хостинг с поддержкой NodeJS (из бесплатных вариантов Yandex Cloud Functions, но с ограничением по функционалу (без таймеров) или Oracle Cloud Free Tier (в чате писали что поднимали там nodejs)), и
- Этот полезный скрипт, который является навыком для Алисы.
Наши действия:
- Создаём обычный приватный диалог (навык);
- Устанавливаем и запускаем скрипт alice-command-skill (код навыка) на сервере/хостинге;
- Создаём нужные нам сценарии умного дома в приложении Яндекс;
- Наслаждаемся новым, недоступным пока в Умном Доме Яндекс, функционалом.
Установка скрипта
На сервере/хостинге выполняем три простых команды (в каждую из строк можно кликнуть мышью и скопировать нужный текст)
- git clone https://github.com/subnetsRU/alice-command-skill.git
- cd alice-command-skill
- npm install
Можно переходить к настройке. Правке файла с настройками - config.js.
После чего запустите навык:
# npm start
Настройка
Если вы пользуетесь браузером Chrome, то для удобства чтения JSON (вам это понадобится, будет присутствовать ниже) на страницах можно использовать плагин JSON Formatter. Если у вас другой браузер, то вы можете найти похожий плагин и для него.
Создаём свой навык в dialogs.yandex.ru (документация):
- Задаём имя навыка (как будем к нему обращаться, в моём примере навык называется "дом вируса", у вас будет своё название);
- В Webhook URL - имя домена или IP-адрес, где расположен скрипт alice-command-skill, и порт (указан в config.js, по умолчанию 8443);
- Указываем, что навык приватный!
- Заполняем остальные обязательные поля;
- Отправляем навык на модерацию, ждём размещения в каталоге (вам придёт письмо-оповещение).
- Добавляем необходимую информацию в config.js:
- Куда логировать:
log: {
type: 'cli', //cli - логировать только в консоль; log - логировать только в файл; all - логировать в консоль и файл;
file: 'acs.log', //название файла с логами, по умолчанию acs.log
},
- Указываем путь до SSL сертификата и его ключа:
ssl: {
crt: '/path/to/ssl.crt', //путь до файла с SSL сертификатом
key: '/path/to/ssl.key', //путь до файла с ключом от SSL сертификата
},
Самоподписанные сертификаты с 2021 года полностью исключены. Валидный сертификат можно бесплатно получить на https://www.sslforfree.com/ или https://letsencrypt.org/ru/
- Указываем порт, на котором будут ожидаться запросы от яндекс диалогов и который мы указали в Webhook URL, например: port: 8443,
- Для авторизации в Яндекс необходимо задать параметры или login и pass или cookie.
- Для того, чтобы ваш логин и пароль от вашего Яндекс аккаунта не был указан в config.js в явном виде, необходимо выполнить:
# npm start auth
После чего вам будет предложено ввести логин и пароль от вашего Яндекс аккаунта:
Yandex login ? my-Yandex-login Yandex password ? my-secret-password Login for config.js: bXktWWFuZGV4LWxvZ2lu Pass for config.js: bXktc2VjcmV0LXBhc3N3b3Jk
После чего полученные значения можно внести в config.js:
login: 'bXktWWFuZGV4LWxvZ2lu', pass: 'bXktc2VjcmV0LXBhc3N3b3Jk',
Отвечаю на немые вопросы:
- введенные вами логин и пароль никуда не отправляются и разработчику данного скрипта они и даром не нужны, даже если будете просить взять - не возьму;
- да, такой способ защиты ваших данных является защитой, как говорят, от дурака и не сокроют эти данные от опытных пользователей, если вы покажете config.js кому-то. В любом случае одним только взглядом в config.js логина и пароля не узнать и именно этого я пытался достичь;
- Значение параметра "cookie" (если не указаны параметры login и pass) можно взять так:
- Открываем консоль браузера (F12, вкладка "Network")
- Заходим, к примеру, в квазар
- Щёлкаем на запрос в консоли
- Копируем из Request Headers часть строки после "Cookie":
cookie: 'yandexuid=XXXXXXXXXXXXXXXXXX;uniqueuid=YYYYYYYYYYYYYYYYYYY;ну и так далее...',
Учтите, что cookie могут устареть и вам придется повторить операцию и обновить значение параметра "cookie".
Если заданы логин и пароль, то параметр "cookie", при успешной авторизации в Яндекс, будет заполнен автоматически.
- Находим идентификатор Яндекс.Станции, которая будет отвечать на команды. Этот ID можно увидеть на странице с устройствами.
- Укажем ID яндекс.станции в config (это только пример! У вас будет другое значение):
speaker: '2077cfa6-2365-4ed4-97d5-4b46895762af',
- Задаём каким устройствам/пользователям разрешено использовать наш навык:
auth:{ user_id: ['BBD3793D182F7C1842F0F1B99B9FE39DFA3E26557C99B7CDE2CAE116A3170301'], application: ['BBD3793D182F7C1842F0F1B99B9FE39DFA3E26557C99B7CDE2CAE116A3170301'], },
Их вы сможете узнать когда выполните своё первое обращение к навыку. Будет отображено в консоли (где вы запустили навык) в incoming JSON, который отображает весь пришедший в навык запрос от Яндекс.Диалогов, где:
- user_id это user.user_id из запроса к навыку - идентификатор пользователя Яндекса, единый для всех приложений и устройств пользователя. Присутствует только, когда пользователь выполнил вход в аккаунт Яндекс на устройстве. Никогда не изменяется.
- application это application.application_id из запроса к навыку - идентификатор экземпляра приложения (браузер или мобильное приложение), в котором пользователь общается с Алисой. Присутствует всегда, но может изменяться (например, если переустановите приложение).
Поясню по user_id и application.
Ситуация 1:
- берём смартфон
- открываем приложение/браузер яндекс
- обращаемся к Алисе: "Алиса, попроси <НАЗВАНИЕ НАВЫКА> помощь"
В этой ситуации в incoming JSON вы увидите наличие только application.application_id, а user.user_id будет отсутствовать
Ситуация 2:
- берём смартфон
- открываем приложение/браузер яндекс
- авторизуемся в приложении/браузере яндекс -> passport.yandex.ru
- обращаемся к Алисе: "Алиса, попроси <НАЗВАНИЕ НАВЫКА> помощь"
- в incoming JSON вы увидите наличие application.application_id и user.user_id
Если в "ситуация 2" использовать ещё один смартфон, то вы увидите, что application.application_id будет отличаться, а user.user_id нет.
Если вы хотите дать возможность управления нескольким устройствам/пользователям, то их можно перечислить через запятую. Пример:
auth:{ user_id: ['BBD3793D182F7C1842F0F1B99B9FE39DFA3E26557C99B7CDE2CAE116A3170301','28D27CB412CAA0AB39DDBD9A6974BE2BCD75362DEFC9C7DE92799D8825EA5399'], application: ['BBD3793D182F7C1842F0F1B99B9FE39DFA3E26557C99B7CDE2CAE116A3170301','8E73021EA84D8432C264A6D75C30F2A654C22D3FCEB22D8BAAD30A807110FD0E'], },
Навык производит аутентификацию по любому из этих параметров. Требуется хотя бы одно совпадение.
- Задайте интенты (слова на которые будет реагировать навык).
- help - фразы для воспроизведения фраз команды "помощь".
- enable - слова, которые будут означать команду для сценариев включения
- disable - слова, которые будут означать команду для сценариев выключения
- timers - слова, которые будут означать команду для работы с таймерами
- action_add - слова, которые будут означать команду для добавления (например таймера)
- action_del - слова, которые будут означать команду для удаления (например таймера)
intents: { help: ['справка', 'помощь', 'что ты умеешь'], enable: ['включи','включить'], disable: ['выключи','выключить'] timers: ['таймер'], action_add: ['добавить','добавь'], action_del: ['удалить','удали'], },
- Задаём с какой периодичностью обрабатывать таймеры:
timer_period: 5,
Значение указывается в минутах. По умолчанию 5 минут.
Выполнение нескольких сценариев за одну команду
Создадим сценарии, которые мы и собираемся выполнять. Сделать это можно как в приложении Яндекса (Android, IOS), так и в web-интерфейсе квазара.
Покажу на примере. Создаем сценарий на включение люстры:
По образу и подобию люстры создаём такой же сценарий, но для лампы.
Создаем сценарий для выполнения двух сценариев:
Открываем страницу с JSON по сценариям находим ID созданных сценариев "Включи люстру" и "Включи лампу" (включением лампы и люстры).
В вносим их в config.js в scenarios -> enable.
Где искать ID у сценария показано на скриншоте:
Аналогично вы можете поступить и со сценариями для выключения, создав сценарии "Выключи люстру" и "Выключи лампу", а затем добавить их в config.js в scenarios -> disable.
scenarios: { enable: { //сценарии на включение 'люстру': '49c84e35-d1f3-4f8b-bc26-fe1ddd383d3f', //сценарий: включи люстру 'лампу': '0f70f28e-6b09-4876-8846-d34449dddc9e', //сценарий: включи лампу }, disable:{ //сценарии на выключение 'люстру': '1e3d27e6-1731-4461-bbe0-e3ed75e41ed5', //сценарий: выключи люстру 'лампу': '212bc2b7-57db-4f79-a896-aff933b0e433', //сценарий: выключи лампу } },
Исходя из примера когда вы скажете Алисе "Включай люстру и лампу" навык запустит два сценария "Включи люстру" и "Включи лампу", что, в свою очередь, приведет к включению устройств "люстра" и "лампа" в вашем Умном Доме.
Далее Вы можете комбинировать, создав сценарии, например:
- Выключи люстру и включи лампу
- Включи люстру и выключи лампу
Таймеры
Рассмотрим на примере самой частой хотелки - автоматическое понижение/повышение уровня громкости станции в зависимости от времени. Для достижения этой цели потребуется:
1. Создать сценарий для понижения громкости:
2. Создать сценарий для повышения громкости:
3. Открыть страницу с JSON по сценариям и найти ID созданных сценариев:
- "Включи ночную громкость"
- "Включи утреннюю громкость"
Копируем ID и вносим их в config.js в scenarios -> enable.
scenarios: { enable: { //сценарии на включение 'ночную громкость': 'd9c96931-03d5-41a3-90d0-e930ff9c0994', 'утреннюю громкость': '39094cf9-9040-4cb9-ba07-b2a669225fa5', }, },
4. Перезапустить навык, т.к. вносились изменения в config.js.
Теперь говорим Алисе, например:
- "попроси <НАЗВАНИЕ НАВЫКА> добавить таймер включи ночную громкость в 23 часа" - навык добавит таймер для ежедневного выполнения сценария "Включи ночную громкость" в 23 часа, что, в свою очередь, приведет к ежедневной автоматической установке громкости станции на 2 в 23 часа.
- "попроси <НАЗВАНИЕ НАВЫКА> добавить таймер включи утреннюю громкость в 8 часов" - навык добавит таймер для ежедневного выполнения сценария "Включи утреннюю громкость" в 8 часов, что, в свою очередь, приведет к ежедневной автоматической установке громкости станции на 5 в 8-мь утра.
Созданные вами таймеры навык будет сохранять в файле timers.json. Пример содержимого файла:
[ { "name":"ночную громкость", "action":"enable", "id":"d9c96931-03d5-41a3-90d0-e930ff9c0994", "days":[0,1,2,3,4,5,6], "hour":"23" }, { "name":"утреннюю громкость", "action":"enable", "id":"39094cf9-9040-4cb9-ba07-b2a669225fa5", "days":[0,1,2,3,4,5,6], "hour":"8", "min":"15", }, { "name":"телевизор", "action":"disable", "id":"3a5550951-c640-468b-a0cc-db5d81dbbe7c", "once":"1594561800", } ]
Где:
- "name" и "action" - для понимания, что это за таймер
- "id" - ID выполняемого сценария
- "days" - дни, по которым будет выполняться сценарий. В Javascript воскресенье это нулевой день, а суббота 6.
- "hour" - час, в который будет выполняться сценарий.
- "min" - минуты, когда будет выполняться сценарий. Если минуты не указаны, то это означает ноль минут.
- "once" - признак одноразового сценария, который будет выполнен в указанное в параметре время (unixtime).
Шаблоны команд, которые нужно использовать для добавления таймера:
- попроси <НАЗВАНИЕ НАВЫКА> добавить таймер <ДЕЙСТВИЕ> <НАЗВАНИЕ СЦЕНАРИЯ> в X часов - запуск ежедневно в X часов ровно.
- попроси <НАЗВАНИЕ НАВЫКА> добавить таймер <ДЕЙСТВИЕ> <НАЗВАНИЕ СЦЕНАРИЯ> в X часов Y минут - запуск ежедневно в X часов Y минут.
- попроси <НАЗВАНИЕ НАВЫКА> добавить таймер <ДЕЙСТВИЕ> <НАЗВАНИЕ СЦЕНАРИЯ> по будням в X часов - запуск по будням в X часов ровно.
- попроси <НАЗВАНИЕ НАВЫКА> добавить таймер <ДЕЙСТВИЕ> <НАЗВАНИЕ СЦЕНАРИЯ> по выходным в X часов Y минут - запуск по выходным в X часов Y минут.
- попроси <НАЗВАНИЕ НАВЫКА> добавить таймер <ДЕЙСТВИЕ> <НАЗВАНИЕ СЦЕНАРИЯ> в <НАЗВАНИЕ ДНЯ (или дней) НЕДЕЛИ> в X часов - запуск по перечисленным дням в X часов ровно.
- попроси <НАЗВАНИЕ НАВЫКА> добавить таймер <ДЕЙСТВИЕ> <НАЗВАНИЕ СЦЕНАРИЯ> через Y минут - одноразовый запуск через Y минут.
- попроси <НАЗВАНИЕ НАВЫКА> добавить таймер <ДЕЙСТВИЕ> <НАЗВАНИЕ СЦЕНАРИЯ> через X часов Y минут - одноразовый запуск через X часов Y минут.
- попроси <НАЗВАНИЕ НАВЫКА> <ДЕЙСТВИЕ> <НАЗВАНИЕ СЦЕНАРИЯ> через Y минут - одноразовый запуск Y минут.
Примеры установки таймеров согласно шаблонам:
- попроси дом вируса добавить таймер включи лампу в 21 час
- попроси дом вируса добавить таймер выключи люстру в 21 час 10 минут
- попроси дом вируса добавить таймер включи коридор по будням в 8 часов
- попроси дом вируса добавить таймер выключи коридор по будням в 8 часов 10 минут
- попроси дом вируса добавить таймер выключи телевизор по понедельникам средам пятницам в 22 часа
- попроси дом вируса добавить таймер включи аквариум в субботу в 9 часов 15 минут
- попроси дом вируса добавить таймер выключи аквариум через 4 часа 30 минут
- попроси дом вируса добавить таймер выключи телевизор через 15 минут
- попроси дом вируса выключить телевизор через 5 минут
Команда для удаления таймера аналогична его добавлению, но вместо "добавить" нужно произнести "удалить". Ну или просто удалить таймер вручную - отредактировав файл timers.json.
Одноразовый таймер удаляется сразу после его исполнения.
Узнать какие таймеры установлены можно командами:
- попроси <НАЗВАНИЕ НАВЫКА> список таймеров
- попроси <НАЗВАНИЕ НАВЫКА> какие установлены таймеры
После правки timers.json навык перезапускать НЕ надо. Файл timers.json будет перечитан автоматически при следующей обработке таймеров, которая производится каждые X минут (где X это значение указанное вами в timer_period в config.js).
Послесловие
Если возникают проблемы с выполнением сценария, то необходимо включить логи и смотреть, что происходит при выполнении сценария, по ним. Для быстрого понимания можно воспользоваться командой:
- попроси <НАЗВАНИЕ НАВЫКА> отчёт
Навык зачитает статус крайнего выполненного сценария. Так статус "200" - все ОК. Статус "403" или "302" - проблема с авторизацией.
Видеодемонстрация работы навыка:
Внимание: После внесения изменений в файл config.js (настройки навыка) необходимо перезапускать скрипт навыка.
С автором статьи можно связаться в телеграм: @VirusNet.