Реалізація удаленого запуску двигуна через мобільний додаток
Удалений запуск двигуна — одна з найбільш технічно відповідальних функцій в автомобільному додатку. Випадковий або несанкціонований старт двигуна — це загроза безпеці людей поблизу машини. Це не 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 телематичного блока.







