Реализация добавления IoT-устройства по серийному номеру в мобильном приложении
Добавление по серийному номеру — резервный способ для случаев, когда QR-наклейка повреждена или её нет на устройстве. Пользователь вводит или сканирует серийный номер вручную, система находит устройство и привязывает его к аккаунту. Задача простая по сути, но требует аккуратной валидации и хорошего UX формы ввода.
Форматы серийных номеров
Серийный номер — не произвольная строка. У каждого производителя свой формат:
-
SN-XXXXXXXX— 8 hex-символов после префикса -
AAAA-BBBB-CCCC-DDDD— группы по 4 символа (аналог ключа активации) - MAC-адрес как серийный номер —
AA:BB:CC:DD:EE:FF - Числовой код —
12345678901
Формат нужно знать заранее — он определяет маску ввода и валидатор. Если серийный номер всегда 12 символов, пользователь не должен угадывать — поле ввода должно показывать маску и принимать только нужный формат.
UI формы ввода
Ключевые требования к полю серийного номера:
Автокапс и автокоррекция — выключить. inputType="textNoSuggestions|textCapCharacters" на Android. На iOS: autocorrectionType = .no, autocapitalizationType = .allCharacters. Автокоррекция превращает ABC123 в Abc123 — устройство не найдётся.
Маска ввода. Для формата XXXX-XXXX-XXXX — вставлять дефисы автоматически по мере ввода. На Android: TextWatcher с обработкой позиции курсора:
editText.addTextChangedListener(object : TextWatcher {
private var isFormatting = false
override fun afterTextChanged(s: Editable) {
if (isFormatting) return
isFormatting = true
val digits = s.toString().filter { it.isLetterOrDigit() }.uppercase()
val formatted = digits.chunked(4).joinToString("-").take(14)
s.replace(0, s.length, formatted)
isFormatting = false
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
Сканирование камерой как альтернатива вводу. Серийный номер часто напечатан штрихкодом на задней панели устройства. Кнопка «Сканировать» рядом с полем ввода. ML Kit или ZXing для Code 128 / Code 39.
Валидация перед запросом к API
Локальная валидация до отправки запроса:
fun validateSerialNumber(input: String): ValidationResult {
val clean = input.filter { it.isLetterOrDigit() }.uppercase()
return when {
clean.length < 8 -> ValidationResult.TooShort
clean.length > 16 -> ValidationResult.TooLong
!clean.matches(Regex("[A-Z0-9]+")) -> ValidationResult.InvalidChars
else -> ValidationResult.Valid(clean)
}
}
Показывать ошибку валидации inline — под полем ввода, не в alert. Пользователь видит проблему сразу и исправляет, не теряя введённые данные.
API: поиск и привязка устройства
Двухэтапный процесс:
Шаг 1 — поиск устройства:
GET /api/devices/lookup?serial=ABC12345678
Ответ: тип устройства, модель, статус (свободно / уже привязано к другому аккаунту / не существует). Показать пользователю что именно найдено — «Датчик температуры модель T200» — до подтверждения привязки.
Шаг 2 — привязка:
POST /api/devices/claim
{ "serial": "ABC12345678", "name": "Датчик на балконе" }
Серийный номер не равен claim-токену — это разные вещи. Серийный номер публичный, по нему находят устройство. Привязка требует аутентификации пользователя (JWT в заголовке), иначе любой мог бы угнать устройство.
Обработка ошибок
| Статус | Что показать |
|---|---|
| 404 Not Found | «Устройство с таким серийным номером не найдено. Проверьте ввод» |
| 409 Conflict | «Это устройство уже привязано к другому аккаунту» |
| 422 Unprocessable | «Неверный формат серийного номера» |
| 503 | «Сервис временно недоступен. Попробуйте позже» |
Для 409 — предложить «Это ваше устройство?» с кнопкой обращения в поддержку. Иначе пользователи с купленными б/у устройствами окажутся в тупике.
Реализация добавления по серийному номеру с валидацией и двухэтапной привязкой: 1 неделя. Стоимость рассчитывается индивидуально.







