Управління IoT-пристроями голосом через мобільний додаток
Голосове управління IoT-пристроями у мобільному додатку — це не просто «додати SiriKit» або «інтегрувати Google Assistant». Це окремий слой логіки: розпізнавання мови, видалення намірів, маппінг на команди пристроїв, зворотній зв'язок. Кожен з цих кроків ломається по-своєму.
Два принципово різні підходи
Вбудовані голосові асистенти (Siri Shortcuts, Google Assistant Actions) працюють через облако та потребують явного дозволу. Siri Shortcuts на iOS доступні через INPlayMediaIntent та INSendMessageIntent, але для довільних IoT-команд потрібен AppIntent (iOS 16+) — Swift-фреймворк з описанням інтентів. Приклад: «Ей Siri, вимкни світло на кухні» → Siri викликає TurnOffLightIntent у вашому додатку, який відправляє MQTT-команду. Затримка — 2–4 секунди через облако Apple, немає гарантій при відключеному інтернету.
Локальне розпізнавання — інший рівень. На iOS це SFSpeechRecognizer з SFSpeechAudioBufferRecognitionRequest. З iOS 13 підтримує on-device режим (requiresOnDeviceRecognition = true) без відправки аудіо в облако. На Android — SpeechRecognizer API (через облако Google) або Vosk / Whisper.cpp для повністю офлайн-розпізнавання.
Для IoT-додатків, де важлива робота в локальній мережі без інтернету, вибір очевидний — локальне розпізнавання + офлайн NLU.
NLU: від тексту до команди пристрою
Розпізнали «включи світло на кухні та підніми температуру до двадцяти двох» — тепер потребує видалити:
- інтент:
turn_on,set_temperature - сутності:
device_type=light,location=kitchen,device_type=thermostat,value=22
Для простих кейсів хватає rule-based підходу: словник глаголів-намірів + словник пристроїв та кімнат з бази користувача. Будуємо регулярки або simple intent matcher на той же список пристроїв що вже є у системі.
Для складних сценаріїв — Rasa NLU (self-hosted) або Duckling для числових значень. На Flutter інтегруємо через HTTP-запит до локального сервера у домашній мережі або через dart:ffi для вбудованої моделі.
Реальний приклад: проект умної квартири, 35 пристроїв, російська мова. Обучили просту модель на fastText з ~500 прикладів команд, конвертували у .tflite, запустили через tflite_flutter. Точність на побутових командах — 94%. Промахи були на складних командах (два дії у одній фразі) — вирішили предобробкою через розбивку по спілучиям «і», «потім», «потім».
Зворотний зв'язок та edge cases
Push to talk vs always-on. Always-on на мобільному — убійця батареї. Рекомендуємо push-to-talk кнопку у додатку + опціональне wake word через Porcupine SDK (PicoVoice). Porcupine працює локально, споживає <5% CPU на idle.
Що робити, якщо пристрій не розпізнано? Не мовчати. Повернути голосовий відповідь через AVSpeechSynthesizer (iOS) / TextToSpeech (Android), перерахувати що було розуміно, попросити уточнити. Користувач не бачить екран — йому потрібен аудіо-зворотний зв'язок.
На Flutter використовуємо flutter_tts для синтезу та speech_to_text як unified API поверх платформенних рушіїв. Важливо: на Android 11+ SpeechRecognizer потребує дозволу RECORD_AUDIO з явним обґрунтуванням у onRequestPermissionsResult. Без внятного rationale — Google Play консоль помічає як нарушення політики.
Інтеграція з MQTT
Голосова команда → NLU → команда пристрою → публікація у MQTT-топік. Затримка від нажатієї кнопки до відклику пристрою: розпізнавання на пристрої ~300–800ms, NLU ~50ms, MQTT publish < 50ms при локальному брокері. Усього — ощущается как мгновенный отклик.
При хмарному розпізнаванні додаємо 1.5–3 секунди. На російській мові облачний Google Speech-to-Text працює добре, Apple Speech — гірше на специфічних IoT-термінах вроде «диммер», «ресівер», «реле».
Терміни
Push-to-talk з хмарним розпізнаванням та простим маппінгом команд — 2–3 тижні. Офлайн-розпізнавання + NLU + wake word + TTS зворотний зв'язок — 6–10 тижнів. Стоимость зависит от языков, платформ и требований к офлайн-работе.







