31 декабря в два часа дня в нашей семье состоялся внеочередной стендап, на котором было принято решение произвести деплой семьи в новую квартиру до наступления Нового Года. Ремонт квартиры, за исключением таких мелочей как шкафы и шторы, был уже закончен, альфа-тестирование произведено. Да и располагается она в 200 метрах от старой, так что релиз выглядел безопасным. К концу новогодних праздников бета-тест квартиры был признан успешным, откат релиза не потребовался.

Привет, Хабр! Меня зовут Иван Садовой, я техлид продукта Авиа в Туту. Но в этой статье расскажу не о работе, а об одном pet-проекте, в котором столкнулся с нестандартной задачей: организовать у себя дома голосовое управление освещением.
Мой рассказ пойдёт в большей степени об организации работы в соответствии с принципами системной инженерии. Но и те, кому интересны технические подробности, смогут найти для себя информацию об устройстве экосистем умного дома.
Оговорюсь, что вовсе не эксперт по проектированию и созданию подобных систем, а самый обычный юзер.

После переезда в новую квартиру самым уютным местом был признан уголок в гостиной перед телевизором. Особую теплоту ему придаёт полукруглый торшер, найденный нашими дизайнерами на просторах маркетплейсов. Торшер управляется пультом, который позволяет регулировать температуру и яркость света.
Я не люблю, когда дома в каждой комнате лежит по три-четыре пульта от всякой техники. В идеале мне хотелось бы, чтобы во всей квартире на виду был один пульт от телевизора, а все остальные убраны в специальный ящик навсегда. Так они точно не потеряются, и их никто не сгрызёт.
Но любую технику нужно хотя бы иногда включать. На мой взгляд, для освещения нет ничего лучше обычного клавишного проходного выключателя (за исключением нескольких декоративных сценариев). А технику типа кондиционеров и электроприводов штор мы сразу выбирали с интеграцией с Алисой, чтобы управлять голосом.
Почему не сработало стандартное решение
Стандартный путь подключения к Алисе чего-нибудь неумного с пультом — это купить умный пульт и обучить его: записать на него сигналы, которые отправляются пультом устройства. Тогда в приложении умного дома появляется виртуальное устройство, и с ним можно работать как с обычным.
Умный пульт у меня под рукой был. Однако стандартный путь не сработал: он упорно не воспринимал сигналы с пульта торшера. Я решил проверить, точно ли торшер управляется ИК-сигналом. На просторах интернета нашёл подтверждение: он работает на RF433. Я мог бы и сам догадаться: на торшере снизу есть коробочка с приёмником, и пульт работает, даже когда эта коробочка стоит под диваном. А диван, как известно, для ИК-сигнала не прозрачен.
На одном известном маркетплейсе я приобрел умный пульт, который поддерживает и ИК, и радиосигналы. Ещё до покупки у меня возникли сомнения, что он подружится с Алисой. Так и вышло: пульт легко подключился к приложению SmartLife от Tuya и обучился сигналам с пульта торшера. Но Умный Дом Яндекса упорно не обнаруживал ни сам умный пульт, ни виртуальный торшер. Попытки интеграции через другие прослойки дали аналогичный результат.
Есть и другие модели умного пульта с RF433, но, судя по отзывам, с ними всегда как повезёт. Кто-то пишет, что радиомодуль не работает вообще (возможно, его просто нет), кто-то тоже не может соединить с Алисой, а у кого-то всё замечательно. Видимо, зависит от китайского подвальчика, в котором собран конкретный пульт.
Я мог бы ограничиться управлением через SmartLife, сказать, что это MVP, установить SmartLife на телефон не только себе, но и супруге. А потом через полгода объявить, что фаза MVP пройдена: MVP больше не MVP, а целевое решение. Но я же не на работе :) И всё-таки решил добиться изначальной цели — сделать голосовое управление.
Собираем контекст: требования, ограничения и риски
Ключевые функциональные требования мы с супругой сформулировали так:
Торшер выключается и включается с помощью голосовой команды.
Голосовой командой можно изменить яркость торшера. Причем использовать мы планируем относительные значения, а не абсолютные: «сделать поярче», а не «установить яркость на 80%».
Голосовой командой можно изменить температуру света: как минимум, поддерживать переключение между тёплым и дневным. В отличие от предыдущего требования, здесь как раз можно использовать абсолютные значения.
Было ещё несколько менее важных функциональных требований вроде возможности создавать сценарии: например, автоматически включать торшер вечером и выключать ночью.
Список ограничений получился таким:
Длительность исполнения голосовых команд не должна превышать 3 секунды (опытным путем определенная задержка, после которой начинаешь кричать на Алису).
Дополнительные финансовые расходы, превышающие символическую сумму в 500 рублей в год, недопустимы (ограничение также известно под кодовым названием «жаба душит»).
Решение не должно быть совсем «дырявым» в плане безопасности, чтобы мне не пришлось отключать его из-за того, что кто-то каждую минуту мигает моим торшером.
Временное решение должно быть запущено в течение недели (это требование от меня, а то неудобно включать торшер, чтобы разрабатывать при его свете решение на постоянку).
Решение должно разрабатываться в вечернее время после 22:00 (это требование от годовалой дочки: с 19 и до 22 папа принадлежит ей и маме).
Процесс разработки решения должен быть интересным для команды разработки в моем лице для поддержания позитивной атмосферы.
Последний пункт я — заказчик продукта и владелец бюджета времени команды разработки в одном лице — своим решением определил как ключевой.
Ещё мы определили допустимые риски:
Если интернет в квартире пропадёт, голосовое управление работать не будет.
Если электричество отключится, то настройки яркости и температуры света сбросятся до дефолтных.
Выбираем решение
Я рассматривал следующие варианты решения.
Купить умный RF-пульт с поддержкой Алисы. Этот вариант не подошёл под требование про интересный процесс разработки. А ещё он требовал финансовых расходов с неизвестным временем получения результата: пришлось бы перебрать несколько моделей, чтобы найти ту, которая заработает с Алисой.
Вооружиться паяльником и добавить в торшер WiFi-модуль, а после интегрировать Алису уже с WiFi-устройством. Такой способ сделал бы сам торшер умным, но в последний раз я держал паяльник в руках в третьем классе. Если бы я в итоге пошёл по этому пути, то наверняка стал бы серийным убийцей торшеров.
Развернуть решение типа Home Assistant и через него интегрировать умный RF-пульт с Алисой. Это интересный в реализации вариант, он ещё и позволяет сохранить работоспособность при отключении интернета. Но запускать Home Assistant на ноутбуке означает, что ноут становится стационарным компьютером, который нельзя выносить из дома, а это неприемлемо. А покупка какой-нибудь малинки явно не влезает в бюджет.
В итоге я выбрал четвёртый вариант: реализовать интеграцию Алисы и умного RF-пульта программно с помощью уже существующих сторонних систем или же написать такую систему самостоятельно. Это тот класс задач, с которым сталкиваюсь каждый день на работе и с которым лучше всего умею работать.
Этап 1: пробуем использовать сторонние решения — IFTTT и Домовёнок Кузя
Погуглив, как можно интегрировать Умный Дом Яндекса и SmartLife, я нашёл вот такую статью. Она уже устарела, но из неё я узнал о двух сервисах: IFTTT и Домовёнок Кузя.
Если кратко, то схема следующая.
Домовёнок Кузя — это навык для Алисы. У него есть админка, в которой можно создать обработчики активационных фраз. Обработчики представляют собой HTTP-запросы, MQTT-сообщения или запросы к IFTTT (по сути, частный случай HTTP-запроса). Кроме этого, есть возможность создавать виртуальные устройства, которые дальше можно подтянуть в Умный Дом Яндекса и использовать для них нативные команды Алисы. Обработка команд происходит с помощью всё тех же обработчиков — нужно лишь сопоставить их друг другу.
IFTTT (расшифровывается как if this, then that) — это сервис, который позволяет по событию в одной системе запускать обработчики в другой. Такая связка в сервисе называется апплетом.
Например, можно создать апплет, который будет получать из Gmail уведомление о новых письмах и отправлять сообщение в телеграм. Есть там и возможность принимать произвольные HTTP-запросы через их собственный сервис Webhooks. В моём случае как раз требовалось создать апплет, который принимает через Webhooks запросы и вызывает сервис SmartLife, интегрированный с моим аккаунтом. Таким образом, можно послать команду в экосистему SmartLife на выполнение какого-нибудь действия на моих устройствах или на запуск сценария.

В итоге получается связка: Алиса слушает голосовую команду, сопоставляет её с виртуальным устройством и конкретной командой на изменение того или иного свойства этого устройства и посылает запрос в Кузю. Кузя генерирует HTTP-запрос в IFTTT, IFTTT ловит его в навыке Webhooks и посылает команду на выполнение сценария в SmartLife, который уже выполняет на умном пульте конкретную команду.
Однако функционал Webhooks оказался платным и в обозначенный бюджет не вписывался. Кроме того, нужно ещё найти подходящую иностранную карту для оплаты. Так что я активировал недельный триал, настроил управление, чтобы уже можно было пользоваться хотя бы в течение этой недели, и продолжил искать полностью устраивающее меня решение.
Мне требовалось найти способ подключиться к экосистеме SmartLife и запустить где-либо обработчик запросов от Кузи. Ещё я отметил для себя: раз Кузя умеет создавать виртуальные устройства Яндекса, то при желании и я смогу. Интеграция через Кузю стала бы решением, которое можно быстро запустить, оценить соответствие требованиям и развивать дальше, если потребуется.
Этап 2: экспериментируем с платформой разработки Tuya
Довольно быстро я нашёл Tuya Developer Platform и среди её возможностей обнаружил то, что мне нужно, — Cloud Projects. Облачный проект представляет собой набор ресурсов: устройства, данные и набор API для доступа и управления ими. Устройства можно добавлять из сторонних приложений и из приложения SmartLife из экосистемы Tuya. Я воспользовался этой возможностью и добавил в проект устройства из своего аккаунта SmartLife.
Для облачного проекта нужно выбрать используемые API. Для моей задачи оказалось достаточно двух API: Authorization Token Management для авторизации в проекте и IoT Core для управления устройствами и вызова сценариев. Использование IoT Core вообще-то платное, но мне как индивидуальному разработчику без проблем продлили триал-период по запросу.
Я намеревался с помощью API непосредственно отправлять команды умному пульту. Для этого мне требовалось найти нужные команды, поддерживаемые пультом, и их данные. Платформа Tuya предоставляет API Explorer, он позволяет удобно экспериментировать с API. Для начала с помощью методов «Get the specifications and properties of the device» и «Get Category List» я определил, к каким категориям устройств относятся мой пульт и виртуальный торшер. Пульт ожидаемо оказался Universal Remote Control, а торшер — недокументированной категорией rf_diy. Это явно служебное обозначение некатегоризированных виртуальных устройств под управлением RF-пульта.
Дальше я намеревался получить информацию о доступных командах для этих устройств с помощью методов «Get the instruction set of the device» и «Get the instruction set of the category», но никакой конкретной информации извлечь не удалось. Для торшера это было ожидаемо, а про пульт я узнал только то, что он поддерживает команду ir_sent с параметром типа string.
Я испробовал обходной способ получения команд. В платформе Tuya есть возможность просмотреть логи устройства, в том числе логи исполнения команд от приложения SmartLife.
Так что я выполнил две команды — уменьшения и увеличения яркости, которым заранее обучил RF-пульт, — и получил такие логи:
{
"rf_type": "sub_2g",
"mode": 0,
"key1": {
"times": 6,
"intervals": 0,
"delay": 0,
"code": {
"lv": 0,
"data": "mi+XAYME4QR3AbYBjASuAYME4gR4AcEEmAG1AYME4QT7AT8ElgHCBHcB4gR3AZYBowTDBJYBwwR3AeEEdwHDBJcBtAGEBLYBhAS1AYQEtgGEBOgEcAHjBHYB4gRYAeMEdQG2AQ==",
"fre": 1
}
},
"feq": 0,
"rate": 0,
"control": "rfstudy_send"
}
{
"rf_type": "sub_2g",
"mode": 0,
"key1": {
"times": 6,
"intervals": 0,
"delay": 0,
"code": {
"lv": 0,
"data": "/S62AaMEwwR3AbUBhAS2AYQE4gR3AeMEVgHVAYQE4gR3AcQEdgHhBHcBwgSWAbYBowTDBFgB4gR2AeMElQHDBHYBuAGCBLUBhAS2AaMEoAGaBMIEdwHiBFgB1AGOBNgEdwG2AQ==",
"fre": 1
}
},
"feq": 0,
"rate": 0,
"control": "rfstudy_send"
}
Команда задаётся в параметре data, но непонятно, что именно передаётся. Я ожидал, что увижу что-то типа идентификатора сохраненного в пульт сигнала, но это явно не так. Если представить эти данные в бинарном виде, то это 100 байт, и в них прослеживается какая-то структура. Неужели запись сигнала хранится в облаке, и на пульт передается прямо последовательность байт, которую надо передать?

Я решил не связываться с неизвестным форматом, живущим по неизвестным правилам. Пусть этой проблемой занимается сама Tuya, а я могу работать с более высокоуровневыми объектами, которые предоставляет мне SmartLife — сценариями.
Я сделал по сценарию на каждую команду RF-пульта, после чего в API Explorer получил список привязанных к дому сценариев при помощи запроса «Scene Linkage Rules → Query Linkage Rules». Так я получил список сценариев и их идентификаторы. Затем запустил сценарий включения светильника методом «Trigger Tap-to-Run». Этот способ сработал — мне удалось решить проблему подключения к системе SmartLife.
Этап 3: разрабатываем промежуточное решение с помощью Cloud Functions
Для работающего решения мне требовалось создать промежуточное звено между Домовёнком Кузей и моим проектом в Tuya Developer Platform.
Я решил для этого использовать решение Cloud Functions от облачной платформы Яндекса по ряду причин:
Оно хорошо подходит по смыслу: приходит запрос, контейнер с кодом запускается, отрабатывает и умирает.
На моих задачах даже при сотнях команд на управление торшером в день это решение остаётся бесплатным.
Мне хотелось попробовать поработать с AWS-подобными платформами. На работе я каждый день разрабатываю продукт в облаке, но там чистый Kubernetes.
Я сразу предполагал, что следующим шагом будет замена Кузи на собственный навык Алисы, а они разворачиваются в Yandex Cloud.
Не буду расписывать процесс разработки облачной функции: он довольно тривиален и хорошо документирован в мануалах.
С точки зрения кода там тоже всё просто:
Принимаем запрос от Кузи, разбираем параметры, ставим им в соответствие то или иное название сценария SmartLife.
Авторизуемся в облачном проекте Tuya.
Получаем по API Tuya идентификатор сценария и запускаем его.
Для оптимизации можно воспользоваться небольшим хаком. Значительную долю времени в моём обработчике занимает получение авторизационного токена в Tuya и загрузка списка сценариев. Если сохранять эти данные хотя бы ненадолго, то можно переиспользовать их при обработке последовательности запросов типа: «Включи торшер» — «Включи мягкий свет» — «Убавь яркость».
Можно настроить облачное хранилище, но я нашёл способ проще. Облачную функцию можно настроить так, чтобы она умирала не сразу после обработки запроса, а после обработки не более чем N запросов в течение не более чем M секунд. Я выставил настройку на 10 запросов за 120 секунд.

Получившееся решение не удовлетворяло требованиям в трёх аспектах.
Кузя не поддерживает команды на относительное изменение свойств, в моем случае — яркости. Когда я даю команду «Сделай ярче», Алиса пытается определить текущее значение яркости и прибавить к нему дельту. Но у моего торшера нельзя узнать, какова сейчас его яркость. Можно было бы сделать хранение значения в облаке, но потребовалось бы прикручивать хранилище. А ещё нужен был бы способ инвалидировать сохраненное значение, если, например, моргнуло электричество.
Исполнение команд происходило заметно медленнее, чем у нативных устройств умного дома. Типичное время составило примерно 4 секунды для первого запроса и 3.5 секунды для последующих, тогда как у нативных устройств оно чуть больше 2 секунд. Примерно 0.5-0.8 секунд работает последовательность запросов к Tuya в функции, и ещё около секунды потерь даёт Кузя.
Облачную функцию пришлось сделать публичной. Это значит, что если кто-то найдёт адрес этой функции, то сможет без ограничений слать в неё запросы. Маловероятно, что кто-то захочет этим заниматься, но всё-таки это слишком «дырявое» решение.
Разрабатываем финальное решение: собственный навык для Алисы
Чтобы исправить эти недостатки, я решил разработать собственный навык Алисы, который умел бы работать с относительными значениями и экономил время за счёт отсутствия одной сетевой интеграции. Это приватный навык — он доступен только в аккаунте создателя.
Навыки Алисы создаются через платформу Яндекс Диалоги. Для моей задачи нужен специальный тип навыка «Для умного дома». Он позволяет описать устройства, которые можно добавить в свой умный дом, их свойства и умения и реализовать применение этих умений.
В документации есть примеры описания различных типов устройств. В моём случае — лампочки с управлением яркостью и диапазоном температур белого цвета. Поддерживается относительное изменение свойства на заданную дельту с помощью параметра relative в запросе.
Обработчиком навыка выступает облачная функция Yandex Cloud, как и в реализации через Домовёнка Кузю. Её логика практически не отличается от предыдущей версии, но добавляется довольно значительный объём кода для описания виртуального устройства в моделях Умного Дома Яндекса. Для локального тестирования и отладки я дополнительно настроил простейший веб-сервер.
Итоговая схема интеграции выглядит так:

Код функции на Go можно посмотреть на моем гитхабе.
Типичное время исполнения команд заметно улучшилось по сравнению с предыдущим решением и составило примерно 2.7 секунды для первого запроса и 2.2 секунды для последующих. А это уже неразличимо со временем исполнения команд нативными устройствами.
Облачные ресурсы, используемые для навыков Алисы, Яндекс не тарифицирует, так что за работу решения мне платить не нужно. Хотя, даже если бы тарификация шла по общим правилам, у меня настолько небольшой проект, что ресурсы для него укладываются в бесплатные лимиты.
В итоге мне удалось достичь всех заявленных требований. Теперь, сидя на диване и дописывая статью, я могу лениво сказать: «Алиса, включи торшер».
Уже после окончания проекта я с радостью осознал, что всё это время двигался по довольно чёткому плану последовательных приближений решения к требуемым критериям. Контекст и план не были записаны где-либо на бумаге, как если бы дело происходило на работе, но в голове у меня они всегда существовали. И это помогло мне не забить на полпути на каком-нибудь полуработающем варианте.
Если у вас есть опыт использования принципов из серьезной разработки в pet-проектах, поделитесь в комментариях.
Комментарии (4)
akma
22.05.2025 11:46Мне кажется вы усложняете… У SonOff есть устройство Sonoff 433 RF Bridge R2. Это универсальный пульт, можно «заложить» туда 9 пультов. Устройство видно в Умном доме Яндекса. Управляю им светом и сигнализацией уже лет 5…
greblin Автор
22.05.2025 11:46Да, я и не спорю, что если бы не было цели интересно провести время, то скорее всего я бы в итоге купил работающий из коробки девайс. Это было осознанное решение пойти по более сложному пути
petro_64
Tuya Developer Platform даёт только триал при регистрации, насколько я помню, и в какой-то момент она порадует вежливой просьбой оплаты акка для продолжения работы.
Гораздо лучше использовать полностью локальный биндинг, я например использую Tuya-mqtt, там надо один раз ключи выдрать, после чего устройство управляется напрямую - это гораздо быстрее и надёжнее чем китайские облака.
У меня Алиса тоже подключена к УД, но через самодельный навык и сильно перепиленный локальный Yandex2mqtt (добавил относительные значения, поправил персистенс, поддержка функций для обработки значений прямо из конфига и т.п.), работает отлично и очень быстро, основная задержка на стороне Алисы, дальше обработка команды проходит практически мгновенно, т.к. всё полностью локально.
Если интересно, то весь мой конфиг УД лежит на гитхабе тоже.
greblin Автор
Спасибо большое за ссылки, обязательно покопаюсь. У меня это первый опыт чего-то кастомного в атоматизации дома. Причем случился он достаточно внезапно, а тема оказалась интересной
Tuya уже один раз присылала вежливую просьбу, но согласилась продлить триал - там где-то в форме есть этот пункт.