Реализация группировки IoT-устройств по зонам/комнатам в мобильном приложении
Когда устройств в системе больше двадцати — без группировки приложение превращается в плоский список из ламп, датчиков и реле вперемешку. Пользователь не находит нужное устройство, управление замедляется, жалобы растут. Группировка по зонам и комнатам — не косметика, а обязательный архитектурный элемент для любого IoT-приложения от среднего масштаба.
Как данные о группировке должны жить
Первый вопрос, который определяет всю архитектуру: где хранится соответствие «устройство → комната → зона»? Два варианта — на сервере или локально на устройстве.
Локальное хранение (SharedPreferences, AsyncStorage, Hive) работает ровно до момента, когда пользователь устанавливает второй телефон или передаёт управление другому члену семьи. Группировка теряется. Синхронизировать через iCloud/Google Drive — отдельная боль с конфликтами версий.
Правильный вариант: иерархия на бэкенде. Структура: home → floor → zone → room → device. Каждый уровень — отдельная запись в PostgreSQL с parent_id и position (для сортировки). Устройство может быть привязано только к одной комнате, но комнаты можно объединять в произвольные зоны (например, «Первый этаж» и «Зона ребёнка» могут пересекаться).
CREATE TABLE locations (
id UUID PRIMARY KEY,
home_id UUID NOT NULL,
parent_id UUID REFERENCES locations(id),
type VARCHAR(20) CHECK (type IN ('floor','zone','room')),
name VARCHAR(100),
position INTEGER DEFAULT 0
);
CREATE TABLE device_locations (
device_id UUID NOT NULL,
location_id UUID NOT NULL,
PRIMARY KEY (device_id, location_id)
);
Связь many-to-many между устройством и локациями нужна для сценариев вроде «датчик движения считается частью и коридора, и зоны безопасности».
UI группировки: что работает на практике
На Flutter используем SliverList с SliverAppBar для каждой группы — это даёт плавный scroll с прилипающими заголовками комнат без потери производительности. ExpansionTile для скрытия/раскрытия комнат. Drag-and-drop для переназначения устройств — через ReorderableListView или пакет flutter_reorderable_list.
На React Native — SectionList с stickySectionHeadersEnabled. Для DnD используем react-native-draggable-flatlist или Reanimated 3 с жестами через GestureHandler. Не используем стандартный ScrollView с ручным вычислением позиций — это гарантированный перформанс-кошмар на Android.
Важный UX-момент: статус группы (все включены / частично / все выключены) должен агрегироваться на клиенте из кэша состояний устройств, а не запрашиваться отдельным API-вызовом. Иначе при открытии экрана «Гостиная» — 20 параллельных запросов, 200ms lag, видимый джиттер на iOS.
Агрегацию делаем через Riverpod StateProvider (Flutter) или Zustand selector (React Native): пересчёт статуса группы происходит реактивно при обновлении любого устройства из группы.
Синхронизация в реальном времени
Пользователь переименовал комнату или переместил устройство — изменение должно отобразиться на всех залогиненных устройствах. Реализуем через WebSocket-канал с сообщениями типа:
{ "event": "location_updated", "location_id": "...", "changes": { "name": "Спальня 2" } }
{ "event": "device_moved", "device_id": "...", "from_location": "...", "to_location": "..." }
Клиент обновляет локальный кэш (flutter_riverpod Notifier / Zustand store) без полной перезагрузки списка. Это критично для многопользовательских сценариев — семья управляет одним домом с разных телефонов.
Что ещё стоит учесть
Иконки и цвета комнат — пользовательские, хранятся в locations.metadata (JSONB). Быстрый доступ к любимым устройствам — отдельный раздел favorites со своим порядком, не зависящим от комнатной иерархии. Поиск по имени устройства — индекс на devices.name + pg_trgm расширение для нечёткого поиска.
Процесс и сроки
Проектирование иерархии и API — 3–5 дней. UI с группировкой и базовым DnD — 2 недели. Реалтайм-синхронизация, мультипользовательские сценарии, поиск — ещё 1,5–2 недели. Итого базовая функциональность — 4–6 недель в зависимости от платформы и сложности иерархии.







