Приложение, которое объясняет
Как показать человеку не только кнопку, но и смысл происходящего?
Приложение показывает неопределённость состояния и помогает восстановить управление. Внешний вид кнопки должен соответствовать известному результату, включая отсутствие подтверждения.
Хороший экран отвечает на несколько коротких вопросов: что система сейчас знает, почему выбрано действие, подтверждено ли оно и как вернуть управление человеку. Если результат неизвестен, это должно быть видно без чтения технического журнала.
KMP объединит часть логики Android и iOS, но не отменит различия разрешений, фоновой работы и системного хранилища ключей. Точные библиотеки будут выбраны по отдельным правилам проекта; здесь задаём поведение и границы интерфейса.
KMP позволяет разделять общую логику между Android и iOS, сохраняя платформенные интеграции там, где они действительно различаются. В Koobique общими могут быть доменные состояния, сценарии подключения, обработка команд и представление данных. BLE, защищённое хранилище, разрешения, аудио и жизненный цикл требуют платформенных адаптеров. [54]
Общий код и Rust#
Общий Rust-код полезен для контрактов и проверок, но стандартный UniFFI генерирует Kotlin/JVM и Swift, а не готовый commonMain KMP. Кандидаты: expect/actual над этими двумя границами или Gobley с KMP-привязками. До выбора нужен небольшой опыт на Android и iPhone: cancellation, callbacks, многопоточность, память и доставка ошибок. Слой FFI не должен открывать внутренние указатели Core. [155][154]
Альтернативный путь — отдельный Kotlin-кодек по той же спецификации и golden vectors. Он уменьшает FFI, но создаёт две реализации. Выбор делается после небольшого proof-of-concept: подключение, одно сообщение, отмена соединения и обновление библиотеки на обеих платформах. Не стоит включать всю Rust-сеть в приложение только ради сокращения ста строк Kotlin.
Главный экран и поведение команды#
Плитка света показывает подтверждённое состояние. После нажатия появляется «выполняется». При принятии без результата — «ожидается подтверждение». При обрыве — «результат неизвестен», а не автоматический возврат в OFF. Повтор использует тот же command_id, когда это продолжение прежней попытки.
Карточка температуры показывает возраст данных; карточка присутствия умеет UNKNOWN. Недоступное устройство не исчезает из списка и не выглядит выключенным. История решения доступна через действие «почему»: использованные условия, источник и фактический результат.
Подключение без лабиринта настроек#
Пользователь выбирает «добавить станцию», сканирует QR, подтверждает физическое владение и передаёт сеть. Экран различает ошибочный пароль, отсутствие подходящего диапазона, отказ локальной сети и незавершённую привязку. Подключение устройства — отдельный похожий сценарий, но его результатом является принадлежность конкретной станции.
Разрешения запрашиваются в момент понятной необходимости. Android имеет отдельные требования к Bluetooth-разрешениям, а iOS — к доступу в локальную сеть. Приложение должно объяснять, что оно ищет локальные устройства, а не требовать всё при первом запуске. [56][57]
Хранение ключей#
Android Keystore и Apple Keychain дают платформенные механизмы хранения секретов. Их доступность и ограничения используются через адаптеры, а не заменяются файлом с ключом в обычных preferences. Экспорт резервного ключа владельца — отдельная операция с явным предупреждением, не часть стандартного диагностического пакета. [71][97]
Закреплённая идентичность станции хранится вместе с инсталляцией. Приложение не доверяет новому сертификату только потому, что он пришёл с прежнего IP. Смена ключа требует подписанного перехода или процедуры восстановления с физическим подтверждением.
Фоновый режим#
Нельзя рассчитывать, что приложение постоянно держит BLE, микрофон и сетевой сокет в фоне одинаково на Android и iOS. Android документирует специальные способы фоновой BLE-работы; другие функции имеют собственный жизненный цикл. Поэтому автоматизации живут на станции, а не в мобильном фоне. [83]
Push через внешние службы может быть удобным дополнением, но не основанием безопасности. При отсутствии интернета локальная карточка состояния работает, а внешнее push-уведомление может задержаться. Уведомление об опасном процессе не заменяет локальную защиту оборудования.
Работа с голосом на телефоне#
На первом этапе используется кнопка записи. Аудио передаётся в станцию по защищённой сессии и обрабатывается локально. Это даёт RU/EN-команды без немедленной интеграции микрофонов в куб. Альтернатива — локальное распознавание на телефоне, если выбранный runtime и политика платформы позволяют полностью офлайн-режим.
Приложение показывает распознанный объект и при необходимости уточнение. Различие «свет» / «подсветка» разрешается через модель комнаты. Нельзя молча выполнить случайное ближайшее имя только потому, что STT дал похожую строку.
Сборка и установка личного приложения#
Android-экземпляр должен иметь воспроизводимую подписанную сборку и сохранённый ключ обновления. На iOS заранее проверяются Xcode, provisioning и ограничения выбранного типа аккаунта. Сам факт наличия MacBook не означает бессрочную установку любой development-сборки; текущие условия проверяются по документации Apple. [58]
Приёмка UX#
Дать второму человеку подключить тестовый узел без устной подсказки. Преднамеренно ввести неверный пароль, отказать в разрешении и прервать процесс. Успех — не только подключение, но и понятный способ продолжить без сброса всей инсталляции.
Мобильное приложение связывает состояние переключателя с целью пользователя, неопределённостью и пределами разрешённой автоматизации.
Что видит пользователь#
Экран комнаты содержит актуальные измерения с признаком свежести, оценку присутствия, фактические состояния выходов и выбранный профиль. Системное «неизвестно» отображается отдельно от OFF. При команде показывается продвижение: принята, выход подтвердил, физический результат проверяется.
Для каждой автоматизации доступна краткая причина и возможность отключить её без удаления устройств. Подробный режим раскрывает наблюдения, версии и ограничение, которое изменило решение. Это проектные требования интерфейса; внутренние правила разработки KMP будут согласованы с документами пользователя, а не выдуманы вместо них.
RU/EN и неоднозначность#
«Включи свет», «включи освещение» и “turn on the lights” могут соответствовать одному намерению. «Подсветка» может быть отдельным физическим объектом. Поэтому словарь синонимов связывается с возможностями и пространствами установки, а не глобально заменяет каждое слово «свет» на ID первого реле.
При нескольких подходящих объектах приложение задаёт короткий вопрос: «Основной свет или подсветку стола?» Ответ привязан к текущему запросу и ограничен по времени. Если за это время изменились права или удалилось устройство, старый ответ не запускает прежнее действие.
Локальный тракт речи#
Последовательность сохраняется: захват, определение речевого участка, распознавание, интерпретация, проверка прав, исполнение и ответ. На первом этапе телефон предоставляет микрофон, а станция или существующий ПК выполняет выбранные локальные модели. Встроенный голосовой модуль куба добавляется после испытания акустики и вычислительной нагрузки; аппаратные варианты разобраны в главах 41–42.
Языковая модель нужна для вариативных формулировок и диалога, но не для точного сравнения времени или проверки электрического диапазона. Сложная фраза сначала превращается в атомарные намерения. Непонятный фрагмент не заменяется наиболее удобным действием.
Что считать успехом голоса#
Точность распознавания текста недостаточна. Ошибка в одном слове может перепутать основное освещение и подсветку; грамматическая ошибка может не менять действие. Проверяем точность конечного намерения, долю ненужных уточнений, ошибочные действия и время до физического результата.
Отдельные наборы: русский, английский, смешанная речь, шум вентиляторов, похожие имена, отрицания и составные запросы. Текст из телевизора или воспроизведённой записи не становится командой владельца только потому, что распознаётся уверенно. Риск голосового запуска и методы подтверждения задаются по классу действия.
Обучение пользовательскому смыслу#
Человек может назвать настольную лампу «моя подсветка». Система сохраняет явный локальный псевдоним после подтверждения. Повторяющееся исправление может породить предложение обновить словарь. Не меняем автоматически нагрузку, права и глобальный смысл команд для остальных пользователей.
Приложение связывает человеческую цель с физическим объектом. AI помогает понять речь; авторизация, неоднозначность и проверка результата остаются видимыми и управляемыми.
Обнаружение без подмены доверия#
Сервис объявляется как _koobique._tcp; TXT содержит только ID установки, поддерживаемый профиль и подсказку идентичности. Отпечаток из mDNS не является доверенным pin: любой сосед может объявить похожую запись. Android использует NsdManager, iOS — Bonjour/NWBrowser с необходимыми разрешениями Local Network и списком сервисов. Отказ разрешения показывается отдельно от отсутствия Hub. MulticastLock не назначается всем сборкам автоматически: необходимость зависит от того, используется системное NSD или собственные multicast-сокеты. [159][57]
Один элемент управления показывает текущий snapshot, рядом — выполнение конкретной команды. Поздний APPLIED(A) обновляет историю A, но не заменяет более новый OFF. При UNKNOWN текст объясняет действие: «не удалось подтвердить; проверить состояние», а не «включено». После истечения запроса новая попытка требует текущих условий и нового ID.
В приложении предусмотрены VoiceOver/TalkBack, масштабирование текста и обозначения состояния не только цветом. «Ручной режим» сопровождается ответом, когда вернётся автоматика. Для E0: «до команды “вернуть автоматику”». Технические boot/fence/lease раскрываются только в диагностике, не перегружают основной экран.
Общий микрофон телефона в первом опыте передаёт аудио на локальный speech-worker Hub. Системное распознавание iOS/Android не считается автоматически офлайн; использование Apple Speech требует проверки supportsOnDeviceRecognition и requiresOnDeviceRecognition для конкретного языка. При отсутствии локального режима нет скрытого перехода в облако. [176]
Намерение приложения и команда устройству#
Приложение отправляет пользовательское намерение в Core. Ядро проверяет права, разрешает объект, фиксирует основания и создаёт одну или несколько команд KQIP для узлов. Поэтому пользовательский запрос и попытка исполнения имеют разные идентичности и историю. API приложения должен описывать их связь, чтение snapshot и обновлений, отзыв сессии, ошибки и восстановление подписки. Перенос KQIP в телефон сам по себе этих операций не определяет; до реализации клиента нужен отдельный проверяемый контракт.