Реалізація управління системою контролю доступу (СКУД) через мобільний додаток
Контролери СКУД — Suprema BioStar, HID OSDP, Ironlogic Z-5R, RusGuard — керуються через патентовані API або стандарт OSDP v2. Мобільний додаток у такій системі розв'язує кілька не пов'язаних задач одночасно: моніторинг подій проходу, управління правами (додати/видалити карту), дистанційне відкриття двері, перегляд відео з камери у входу.
Інтеграція з контролерами: REST API vs OSDP
Більшість сучасних СКУД надають REST API поверх своєї БД. Suprema BioStar 2 — типовий приклад: /api/v1/doors, /api/v1/events, /api/v1/users. Аутентифікація через JWT з коротким TTL (звичайно 30 хвилин), рефреш по refresh_token.
Прямий OSDP через TCP — рідкість у мобільних додатках, це скоріше територія промислових інтеграторів. Для мобільного клієнта завжди є API-шар.
Структура типового события прохода з BioStar 2 API:
data class AccessEvent(
val id: Long,
val timestamp: Instant,
val deviceId: Long,
val doorId: Long,
val userId: Long?, // null якщо карта не опізнана
val cardNumber: String?,
val eventCode: Int, // 0=вхід дозволено, 1=вхід заборонено, 2=вихід
val temperature: Double?, // якщо термінал з вимірюванням температури
val imageData: String?, // base64 фото з камери термінала
)
Жива лента событій через WebSocket (BioStar 2 підтримує /ws/events):
class AcsEventStream(private val url: String, private val token: String) {
fun observe(): Flow<AccessEvent> = callbackFlow {
val client = OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS)
.build()
val ws = client.newWebSocket(
Request.Builder().url(url)
.header("Authorization", "Bearer $token").build(),
object : WebSocketListener() {
override fun onMessage(webSocket: WebSocket, text: String) {
val event = Json.decodeFromString<AccessEvent>(text)
trySend(event)
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
close(t)
}
}
)
awaitClose { ws.close(1000, null) }
}
}
Дистанційне відкриття двері
Найчутливіша функція — відкриття двері по нажатию у додатку. Три вимоги: авторизація запиту, аудит дії, захист від replay-атаки.
suspend fun unlockDoor(doorId: Long) {
// Підпис запиту timestamp + doorId + userId щоб виключити replay
val timestamp = System.currentTimeMillis()
val payload = "$timestamp:$doorId:${authService.userId}"
val signature = hmacSha256(payload, authService.apiSecret)
api.unlockDoor(DoorUnlockRequest(
doorId = doorId,
timestamp = timestamp,
signature = signature,
reason = "manual_unlock_mobile",
))
// Подія логується на сервері автоматично через API
}
На сервері перевіряємо: timestamp не старший за 30 секунд, підпис валідна, у користувача є право на цю дверь. Без цих перевірок перехоплений запит можна відтворити.
Управління правами та картами
Додавання карти користувачу — типова задача для HR або охорони. Форма в додатку: вибір користувача, введення або зчитування номера карти (NFC через android.nfc.NfcAdapter або CoreNFC на iOS), вибір дверей та часового розписання доступу:
Future<void> assignCard(int userId, String cardNumber, List<int> doorIds,
AccessSchedule schedule) async {
await _api.post('/users/$userId/credentials', body: {
'type': 'card',
'card_number': cardNumber,
'doors': doorIds,
'schedule_id': schedule.id,
'valid_from': schedule.from.toIso8601String(),
'valid_until': schedule.until?.toIso8601String(),
});
}
NFC-зчитування номера карти Mifare Classic — зручна фіча: охоронець прикладає карту до телефона замість ручного введення 10-значного числа.
Фото та відео у входу
Термінали з камерою (Suprema FaceStation, ZKTeco SpeedFace) відправляють фото у событі прохода (base64 JPEG ~15-30 KB). Відображаємо в ленті событій через CachedNetworkImage або декодуємо base64 напрямку. IP-камери у входу (Hikvision, Dahua) через RTSP-стрім — у Flutter через flutter_vlc_player або media_kit.
Розробка мобільного додатка для СКУД з лентою событій, дистанційним управлінням дверима та управлінням картами: 4–7 тижнів. Вартість розраховується індивідуально після вивчення API конкретної системи та вимог до безпеки.







