Ранее в этом месяце Pebble выпустил версию 3.6 своего SDK. Мои глаза расширились, когда я обнаружил, что это включает доступ к их Dictation API. Этот API предоставляет всем разработчикам Pebble доступ к диктовке с микрофона Pebble Time. Я с нетерпением открыл редактор CloudPebble и начал экспериментировать!
В этой статье мы рассмотрим API Dictation, собрав приложение watchapp, которое принимает продиктованное сообщение и отправляет его на канал Slack через IFTTT.
Что вам нужно
Чтобы просмотреть это руководство, вам понадобится следующее:
- Часы Pebble Time — у более старых камушков нет микрофона, так что на них это не сработает!
- Общие знания в области разработки приложений Pebble. Если вы новичок в разработке Pebble, прочитайте мое руководство для начинающих по Pebble Watch Development с использованием JavaScript, чтобы начать.
- Счет IFTTT
- Учетная запись Slack и канал Slack для отправки сообщений. Вы можете отключить эту функцию для любой другой службы, с которой IFTTT позволяет отправлять сообщения, если вы не являетесь пользователем Slack.
Код
Есть разработчики, которые предпочитают прыгать прямо в код. Если вы разработчик такого типа, весь код доступен на GitHub .
Наш watchapp будет иметь два основных файла:
-
main.c
— Наш C-код управляет диктовкой, отображением инструкций и отображением введенного сообщения. -
pebble-js-app.js
— это управляет нашим общением с IFTTT.
IFTTT
Я бы порекомендовал сначала настроить канал IFTTT Maker и правило Slack, просто чтобы настроить код, который мы сделаем после этого, имеет больше смысла.
Ранее я освещал IFTTT в статьях « Подключение лампочек LIFX к IoT с использованием IFTTT и подключение IoT и Node.js к IFTTT» (второй рассказывает о канале «Создатель»). Поэтому, если вы хотите получить более подробное объяснение использования IFTTT и канала Maker, ознакомьтесь с ними.
Вот очень быстрый пример того, что вам нужно настроить:
- Создайте учетную запись IFTTT, если у вас ее еще нет.
- Подключите Maker Channel и Slack Channel к своей учетной записи.
- Создайте новый рецепт с каналом Maker в качестве триггера.
- Выберите опцию триггера «Получить веб-запрос» и назовите событие «slack_message».
- Выберите канал Slack, который будет каналом действия для этого правила IFTTT, и выберите действие «Post to channel».
- Выберите канал Slack, на который вы хотите перейти, и установите в сообщении только
{{Value1}}
. - Вы можете удалить заголовок или выбрать что-то вроде «Pebble Message Received». Я оставил это пустым. Я также оставил URL-адрес эскиза пустым.
- Создайте свое действие, и вы должны быть готовы к работе! IFTTT готов отправлять любые сообщения, полученные от этого события, на указанный вами канал Slack.
Наш код C
Файл main.c
выглядит так:
#include <pebble.h> static Window *s_main_window; static TextLayer *message_layer; static DictationSession *s_dictation_session; static char display_message[512]; static void handle_message(char *slack_message) { DictionaryIterator *iter; app_message_outbox_begin(&iter); dict_write_cstring(iter, 0, slack_message); app_message_outbox_send(); } static void dictation_session_callback(DictationSession *session, DictationSessionStatus status, char *transcription, void *context) { if(status == DictationSessionStatusSuccess) { snprintf(display_message, sizeof(display_message), "Message sent!\n\n\"%s\"", transcription); text_layer_set_text(message_layer, display_message); handle_message(transcription); } else { static char error_message[128]; snprintf(error_message, sizeof(error_message), "Error code:\n%d", (int)status); text_layer_set_text(message_layer, error_message); } } static void select_click_handler(ClickRecognizerRef recognizer, void *context) { dictation_session_start(s_dictation_session); } static void click_config_provider(void *context) { window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler); } static void window_load(Window *window) { Layer *window_layer = window_get_root_layer(window); GRect bounds = layer_get_bounds(window_layer); message_layer = text_layer_create(GRect(bounds.origin.x, (bounds.size.h - 72) / 2, bounds.size.w, bounds.size.h)); text_layer_set_text(message_layer, "Press select and tell me your Slack message :)"); text_layer_set_text_alignment(message_layer, GTextAlignmentCenter); text_layer_set_font(message_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); layer_add_child(window_layer, text_layer_get_layer(message_layer)); } static void window_unload(Window *window) { text_layer_destroy(message_layer); } static void init() { s_main_window = window_create(); window_set_click_config_provider(s_main_window, click_config_provider); window_set_window_handlers(s_main_window, (WindowHandlers) { .load = window_load, .unload = window_unload, }); window_stack_push(s_main_window, true); app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum()); s_dictation_session = dictation_session_create(sizeof(display_message), dictation_session_callback, NULL); } static void deinit() { dictation_session_destroy(s_dictation_session); window_destroy(s_main_window); } int main() { init(); app_event_loop(); deinit(); }
Наши диктантные звонки
Первый фрагмент кода, который заметят разработчики Pebble, — это новая строка, которая устанавливает DictationSession
. Это то, что мы храним в наших транскрипционных данных и других данных, связанных с Dictation API, в:
static DictationSession *s_dictation_session;
Далее мы определяем простой массив char
для хранения нашего последнего успешно записанного сообщения:
static char display_message[512];
В нашей функции init()
мы настраиваем фактический сеанс диктовки так, чтобы он был готов и ждал, когда мы его запросим. Он предназначен для предоставления места для хранения нашего диктованного сообщения, соответствующего длине display_message
. Он также устанавливает функцию обратного вызова, как только мы выполнили диктовку, как dictation_session_callback()
.
s_dictation_session = dictation_session_create(sizeof(display_message), dictation_session_callback, NULL);
Запуск диктовки по клику
Мы хотим настроить нашу диктовку на запуск, когда пользователь нажимает кнопку выбора на своем Pebble. Мы устанавливаем функциональность window_set_click_config_provider()
через window_set_click_config_provider()
.
window_set_click_config_provider(s_main_window, click_config_provider);
Это говорит нашему Pebble, что наши функции нажатия определены в click_config_provider()
. В рамках этого мы инициализируем процесс диктовки нажатием кнопки выбора (определяется как BUTTON_ID_SELECT
в SDK Pebble). Это действие кнопки настраивается с помощью функции window_single_click_subscribe()
.
static void click_config_provider(void *context) { window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler); }
Наша select_click_handler()
имеет только одну задачу — начать наш сеанс dictation_session_start()
с помощью dictation_session_start()
:
static void select_click_handler(ClickRecognizerRef recognizer, void *context) { dictation_session_start(s_dictation_session); }
Когда наш сеанс диктовки закончен, и пользователь передал нам свое сообщение через микрофон, мы сказали Pebble запустить dictation_session_callback()
. В этой функции мы начинаем с проверки, была ли диктовка успешной. API предоставляет это нам через переменную DictationSessionStatus status
:
static void dictation_session_callback(DictationSession *session, DictationSessionStatus status, char *transcription, void *context) { if(status == DictationSessionStatusSuccess) { // Dictation worked! } else { // Dictation failed 🙁 } }
Если диктовка прошла успешно, мы отображаем транскрибированное сообщение в приложении (в основном просто для того, чтобы дать пользователю визуальную обратную связь о том, что приложение имеет его сообщение), а затем мы передаем транскрибированное сообщение в handle_message()
(мы рассмотрим, как эта функция работает в ближайшее время ):
snprintf(display_message, sizeof(display_message), "Message sent!\n\n\"%s\"", transcription); text_layer_set_text(message_layer, display_message); handle_message(transcription);
Если есть ошибка, мы отображаем код сообщения об ошибке на экране вместо транскрипции:
static char error_message[128]; snprintf(error_message, sizeof(error_message), "Error code:\n%d", (int)status); text_layer_set_text(message_layer, error_message);
Передача сообщений в JavaScript
Функция handle_message
мы использовали ранее, принимает сообщение и помещает его в словарь нашего приложения. Это позволяет нам отправлять эти данные в код JavaScript с помощью функции Pebble AppMessage. Мы app_message_outbox_begin()
доступ к словарю через DictionaryIterator
а затем открываем наш почтовый ящик, готовый отправить сообщение в наш JavaScript, используя app_message_outbox_begin()
. У нас есть только один ключ в нашем словаре для этого приложения, в котором хранится сообщение диктовки, поэтому, когда мы записываем в наш словарь, мы записываем сообщение в позицию 0
в dict_write_cstring()
. Как только сообщение сохранено в этом ключе, мы отправляем его через app_message_outbox_send()
.
static void handle_message(char *slack_message) { DictionaryIterator *iter; app_message_outbox_begin(&iter); dict_write_cstring(iter, 0, slack_message); app_message_outbox_send(); }
Остальная часть кода на C довольно понятна для всех, кто раньше работал над разработкой приложений для Pebble, поэтому мы продолжим работу с JavaScript!
Наш код JavaScript
Часть JavaScript в уравнении немного короче и выглядит так:
var host = "https://maker.ifttt.com/trigger/", key = "PUTYOURKEYHERE"; function send_message(message) { console.log(host + "slack_message/with/key/" + key); var url = host + "slack_message/with/key/" + key, data = {"value1": message}; sendPOSTRequest(url, data, function() { console.log("Error!"); }); } function sendPOSTRequest(url, data, fallback) { var req = new XMLHttpRequest(); req.onreadystatechange = function(e) { if (req.readyState == 4 && req.status == 200) { var response = JSON.parse(req.responseText); console.log("Response was ", response); if (response !== undefined && !response.error) { console.log("Message sent successfully."); } else { console.log(response.error); if (fallback) fallback(); } } else if (req.status == 404 || req.status == 500) { console.log("Error " + req.status); if (fallback) fallback(); } }; req.open("POST", url); req.setRequestHeader("Content-Type", "application/json"); req.send(JSON.stringify(data)); } Pebble.addEventListener("appmessage", function(e) { var message = e.payload["0"]; console.log("Received message: " + message); send_message(message); });
Начало JavaScript устанавливает начальные настройки URL для URL, который мы хотим вызвать из IFTTT. Этот вызов должен быть в формате https://maker.ifttt.com/trigger/{event}/with/key/{yourkey}
. Мы храним начальную часть URL в host
а ваш ключ — в key
. Вы можете найти свой ключ на странице IFTTT канала Maker . Остальное будет настроено в следующей функции.
var host = "https://maker.ifttt.com/trigger/", key = "PUTYOURKEYHERE";
Функция, которую мы будем вызывать, когда мы хотим отправить сообщение в IFTTT, получила соответствующее имя send_message()
. Это помещает наши переменные host
и key
в наш IFTTT URL вместе с нашим именем события slack_message
. Переменная data
содержит объект JSON с ключом "value1"
который IFTTT ищет, когда решает, как ответить на запрос POST. Если вы хотите передать больше значений, вы также можете передать value2
и value3
.
function send_message(message) { console.log(host + "slack_message/with/key/" + key); var url = host + "slack_message/with/key/" + key, data = {"value1": message}; ...
Затем в нашей функции мы передаем этот url
и data
в функцию sendPOSTRequest()
которая будет выполнять магию HTTP-запроса. Он имеет функцию обратного вызова с ошибкой, которая будет запускаться, если что-то пойдет не так (в этом случае мы просто запишем это на консоль).
sendPOSTRequest(url, data, function() { console.log("Error!"); }); }
Функция sendPOSTRequest()
использует типичный формат JavaScript XMLHttpRequest
. Это будет очень похоже на любые запросы POST, содержащие данные JSON. Объяснение деталей того, как это работает, выходит за рамки данной статьи, но довольно распространено в Интернете:
function sendPOSTRequest(url, data, fallback) { var req = new XMLHttpRequest(); req.onreadystatechange = function(e) { if (req.readyState == 4 && req.status == 200) { var response = JSON.parse(req.responseText); console.log("Response was ", response); if (response !== undefined && !response.error) { console.log("Message sent successfully."); } else { console.log(response.error); if (fallback) fallback(); } } else if (req.status == 404 || req.status == 500) { console.log("Error " + req.status); if (fallback) fallback(); } }; req.open("POST", url); req.setRequestHeader("Content-Type", "application/json"); req.send(JSON.stringify(data)); }
Наконец, у нас есть фрагмент кода, который запускает наше сообщение. Мы слушаем событие appmessage
которое вызывается app_message_outbox_send()
в нашем C-коде. Когда это приложение обнаруживает его, оно отправляет данные в формате {"0": "yourmessage"}
. Мы фокусируемся на ключе "0"
и отправляем его в нашу send_message()
.
Pebble.addEventListener("appmessage", function(e) { var message = e.payload["0"]; console.log("Received message: " + message); send_message(message); });
Запуск нашего приложения
Лучше всего запускать приложение на реальных часах Pebble Time. Вы также можете использовать команды Pebble API в терминале для работы с эмулятором через SDK, однако я обнаружил, что гораздо проще просто поговорить с моим Pebble напрямую через CloudPebble! Главное, что нужно иметь в виду при создании приложения, это убедиться, что «Build Aplite» не отмечен в настройках вашего проекта. Наша демонстрационная программа не будет работать с платформой Aplite (это первая платформа Pebble watchface). Ваши настройки должны выглядеть так:
Приложение в действии!
Когда мы впервые открываем приложение, оно должно выглядеть так:
Затем, когда вы нажмете кнопку выбора (ту, что посередине с правой стороны), экран диктовки Пеббл должен появиться в ожидании вашего голоса. После того, как вы произнесете речь и получите ваше сообщение, он будет использовать Nuance, стороннюю службу, для расшифровки вашего сообщения. Как только он появится, экран диктовки Pebble попросит вас подтвердить правильность сообщения.
Если это так, примите его, и он предоставит этот вклад вашему приложению:
Тогда, если вы посмотрите на свой канал Slack, сообщение также должно появиться там!
Вывод
Новый Dictation API невероятно прост для внедрения в приложение Pebble. Вы можете подключить его к любому количеству веб-API (или другим каналам IFTTT) для выполнения ряда задач. Существует большой потенциал для анализа продиктованного текста сообщения и настройки различных действий в зависимости от того, что говорит пользователь.
Не стесняйтесь использовать этот код в качестве отправной точки для вашей собственной идеи Pebble Dictation. Попробуйте, это невероятно дает возможность собрать голосовое приложение SmartWatch вместе и увидеть его в действии! Если вы что-то делаете из этого урока, пожалуйста, поделитесь им в комментариях или свяжитесь со мной в Twitter ( @thatpatrickguy ). Я хотел бы услышать об этом.
Если вы ищете дополнительные ссылки по разработке Pebble или API Dictation, в частности, у меня есть несколько ссылок на мое руководство по разработке Dev Diner Pebble . Если у вас есть другие замечательные ресурсы, которых у меня нет в списке, пожалуйста, дайте мне знать!