Реализация удалённого запуска двигателя через мобильное приложение
Удалённый запуск двигателя — одна из самых технически ответственных функций в автомобильном приложении. Случайный или несанкционированный старт двигателя — это угроза безопасности людей рядом с машиной. Это не CRUD-операция, это команда с серьёзными последствиями, и архитектура должна это отражать.
Стек: телематический блок + GSM + защищённая команда
Удалённый старт реализуется через телематический блок управления (ТБУ) с реле, подключёнными к цепи запуска автомобиля. Бюджетные варианты — Pandora, StarLine, Scher-Khan с GSM-модулем и API производителя. Кастомные решения для автопарков — Teltonika FMB003/FMB125 с DOUT-выходами и командами через MQTT или SMS.
Pandora/StarLine предоставляют облачный API. Команда запуска:
suspend fun remoteStart(carId: Long): EngineStartResult {
// 1. Проверить предусловия через API
val status = api.getVehicleStatus(carId)
check(!status.isMoving) { "Автомобиль в движении" }
check(status.doorsLocked) { "Двери не заперты" }
check(status.hoodClosed) { "Капот открыт" }
// 2. Запрос с TOTP-подтверждением (или биометрией)
val otp = totpManager.generateOtp(currentUser.secret)
// 3. Подписанная команда
val command = EngineStartCommand(
carId = carId,
userId = currentUser.id,
timestamp = Instant.now().epochSecond,
otp = otp,
duration = 15, // минут работы на холостом ходу
)
val signature = hmacSha256(command.serialize(), currentUser.commandSecret)
return api.sendCommand(command.copy(signature = signature))
}
Биометрическое подтверждение на устройстве
Перед отправкой команды — обязательное подтверждение через BiometricPrompt (Android) или LocalAuthentication (iOS). Не PIN, не пароль — именно биометрия или device credential:
suspend fun confirmWithBiometrics(context: FragmentActivity): Boolean {
val executor = ContextCompat.getMainExecutor(context)
val prompt = BiometricPrompt(context, executor, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
continuation.resume(true)
}
override fun onAuthenticationFailed() {
continuation.resume(false)
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
continuation.resumeWithException(BiometricException(errString.toString()))
}
})
val info = BiometricPrompt.PromptInfo.Builder()
.setTitle("Подтвердите запуск двигателя")
.setSubtitle("Toyota Camry · ${car.plateNumber}")
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG
or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.build()
return suspendCoroutine { continuation = it.also { prompt.authenticate(info) } }
}
На iOS аналог — LAContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics).
Статус выполнения команды и timeout
Команда на запуск отправлена — двигатель не стартует мгновенно. GSM-команда доходит за 2-15 секунд, запуск занимает ещё 3-5 секунд. В UI — прогресс-индикатор с этапами:
enum EngineStartStage {
sending, // команда уходит на сервер
delivered, // сервер подтвердил доставку на ТБУ
cranking, // ТБУ подал сигнал стартеру
running, // двигатель запустился (ignition = on, rpm > 400)
failed, // не запустился за отведённое время
}
Состояние обновляем через WebSocket или polling статуса устройства. Таймаут 60 секунд — если двигатель не запустился, показываем ошибку и отключаем реле стартера (безопасная остановка попытки).
Ограничения безопасности
Команда не выполняется если:
- автомобиль уже движется (скорость > 0 из GPS)
- капот открыт (DINPUT из ТБУ)
- прошло менее 30 секунд с последней попытки запуска
- пользователь сменил пароль или устройство за последние 24 часа (защита от угона аккаунта)
Каждая попытка — в аудит-лог с координатами автомобиля, ID пользователя, устройства, IP и результатом.
Разработка функции удалённого запуска двигателя с биометрическим подтверждением и контролем статуса: 5–8 недель (в составе более широкого приложения). Стоимость рассчитывается индивидуально после анализа API телематического блока.







