Replace geofence polling with native Android geofence
This commit is contained in:
186
README.md
186
README.md
@@ -1,103 +1,85 @@
|
||||
# Ignis App
|
||||
|
||||
Мобильное приложение для управления умными лампами WiZ через self-hosted сервер [Ignis Core](https://git.akokos.ru/artem.kokos/ignis-core).
|
||||
Android-клиент для self-hosted backend [Ignis Core](https://git.akokos.ru/artem.kokos/ignis-core). Приложение управляет группами ламп WiZ, расписаниями, API-ключами и гео-автоматизацией ухода из дома.
|
||||
|
||||
## Возможности
|
||||
## Что умеет
|
||||
|
||||
- **Мульти-дом** -- поддержка нескольких серверов Ignis (квартира, дача, друзья). Каждый дом -- отдельный сервер со своим URL и API-ключом.
|
||||
- **Группы ламп** -- создание, удаление и управление. При создании группы есть product-валидация, автогенерация `ID`, предупреждение о конфликтах по устройствам и более честный перескан сети.
|
||||
- **Управление освещением:**
|
||||
- Включение/выключение
|
||||
- Яркость 10--100% с шагом 10%
|
||||
- Цветовая температура 2700--6500K с шагом 100K
|
||||
- RGB-цвет через HSV-пикер
|
||||
- Сцены (загружаются с сервера, отображаются с человекочитаемыми названиями)
|
||||
- Таймер "включить на 4 часа"
|
||||
- **Расписания** -- одноразовые таймеры с выбором даты/времени и повторяющиеся задачи с выбором дней недели. Просмотр, создание, валидация и отмена активных задач.
|
||||
- **API-ключи** -- просмотр, создание, отзыв и повторная активация гостевых ключей для администраторов с отдельным UX для только что созданного ключа.
|
||||
- **Статистика и лог событий** -- просмотр сводки по группам и последних событий сервера.
|
||||
- **Геофенс и расстояния** -- live-дистанция до дома в UI и опциональное автовыключение света при уходе. Геофенс работает для текущего активного дома, показывает диагностический статус и использует cooldown/re-arm поведение.
|
||||
- **Устойчивость к ошибкам** -- гранулярные состояния загрузки (`LoadState`), централизованная обработка сетевых сбоев, soft-ошибки при управлении ползунками без спама в UI.
|
||||
- несколько домов с отдельными URL и API-ключами;
|
||||
- управление группами света: `on/off`, яркость, температура, RGB, сцены;
|
||||
- таймер "включить на 4 часа";
|
||||
- одноразовые и повторяющиеся расписания;
|
||||
- статистика и лог событий;
|
||||
- управление гостевыми API-ключами для администратора;
|
||||
- расстояние до дома и автовыключение света по geofence.
|
||||
|
||||
## Гео-автоматизация
|
||||
|
||||
Для активного дома приложение может зарегистрировать системный Android geofence. После подтверждённого `EXIT` запускается короткая фоновая задача, которая проверяет группы и выключает только те, что реально включены.
|
||||
|
||||
Это не polling каждые 15 минут. Основной триггер здесь событийный:
|
||||
- geofence регистрируется нативно через Android geofencing API;
|
||||
- сетевое выключение выполняется отдельным one-off worker;
|
||||
- при отсутствии координат или выключенной опции geofence не армится.
|
||||
|
||||
## Стек
|
||||
|
||||
- Flutter 3.x / Dart
|
||||
- Riverpod -- управление состоянием
|
||||
- Dio -- HTTP-клиент
|
||||
- SharedPreferences -- локальное хранение несекретных настроек
|
||||
- Flutter Secure Storage -- безопасное хранение API-ключей
|
||||
- Geolocator -- геолокация
|
||||
- Workmanager -- периодические фоновые задачи
|
||||
- Flutter Local Notifications -- локальные уведомления
|
||||
- Flutter / Dart
|
||||
- Riverpod
|
||||
- Dio
|
||||
- SharedPreferences
|
||||
- flutter_secure_storage
|
||||
- Geolocator
|
||||
- Android Geofencing API
|
||||
- Android WorkManager
|
||||
|
||||
## Структура проекта
|
||||
## Структура
|
||||
|
||||
```text
|
||||
lib/
|
||||
├── app/
|
||||
│ ├── app_bootstrap.dart -- bootstrap приложения и навигация
|
||||
│ ├── build_info.dart -- метаданные сборки (дата, git hash)
|
||||
│ ├── error_message.dart -- форматирование ошибок API и сети
|
||||
│ └── load_state.dart -- универсальный стейт загрузки (idle/loading/data/error)
|
||||
├── main.dart -- точка входа, тема, роутер
|
||||
├── models/
|
||||
│ ├── api_key_info.dart -- типизированная модель API-ключа
|
||||
│ ├── auth_info.dart -- информация об авторизации
|
||||
│ ├── event_log_item.dart -- лог событий
|
||||
│ ├── home_config.dart -- несекретная конфигурация сервера
|
||||
│ ├── ignis_device.dart -- устройство умного дома
|
||||
│ ├── ignis_group.dart -- группа устройств и её состояние
|
||||
│ ├── ignis_scene.dart -- сцена освещения
|
||||
│ ├── schedule_task.dart -- задача расписания
|
||||
│ └── stats_summary.dart -- статистика
|
||||
├── services/
|
||||
│ ├── api_client.dart -- HTTP-клиент к Ignis Core API
|
||||
│ ├── credentials_storage.dart -- безопасное хранение ключей
|
||||
│ ├── geofence_worker.dart -- фоновая логика геофенса
|
||||
│ └── settings_service.dart -- хранение списка "домов"
|
||||
├── features/
|
||||
│ ├── api_keys/providers/ -- управление гостевыми API-ключами
|
||||
│ ├── auth/providers/ -- auth/me и auth-state
|
||||
│ ├── groups/ -- валидация и логика форм групп
|
||||
│ ├── homes/ -- дома, геолокация, geofence sync/runtime
|
||||
│ ├── remote/providers/ -- polling групп, устройства, сцены, control errors
|
||||
│ ├── schedules/ -- логика и providers расписаний
|
||||
│ ├── shared/providers/ -- базовые core providers
|
||||
│ └── stats/providers/ -- статистика и лог событий
|
||||
├── providers/
|
||||
│ └── providers.dart -- compatibility barrel для публичных provider-экспортов
|
||||
├── screens/
|
||||
│ ├── api_keys_screen.dart -- экран гостевых API-ключей
|
||||
│ ├── event_log_screen.dart -- последние события сервера
|
||||
│ ├── homes_screen.dart -- список домов, distance/geofence статус
|
||||
│ ├── home_edit_screen.dart -- создание и редактирование дома
|
||||
│ ├── remote_screen.dart -- основной экран управления светом
|
||||
│ ├── group_edit_screen.dart -- создание группы с выбором устройств
|
||||
│ ├── schedules_screen.dart -- создание и просмотр расписаний
|
||||
│ └── stats_screen.dart -- статистика по командам
|
||||
└── widgets/
|
||||
├── build_info_text.dart -- лейбл с версией сборки
|
||||
├── group_card.dart
|
||||
├── load_error_view.dart -- универсальный виджет ошибок и retry
|
||||
└── color_picker.dart
|
||||
├── app/ # bootstrap, build info, error/load helpers
|
||||
├── features/ # feature-level providers and logic
|
||||
│ ├── api_keys/
|
||||
│ ├── auth/
|
||||
│ ├── groups/
|
||||
│ ├── homes/
|
||||
│ ├── remote/
|
||||
│ ├── schedules/
|
||||
│ ├── shared/
|
||||
│ └── stats/
|
||||
├── models/ # typed domain models
|
||||
├── providers/ # public provider barrel
|
||||
├── screens/ # UI screens
|
||||
├── services/ # API client, settings, credentials
|
||||
└── widgets/ # reusable UI widgets
|
||||
|
||||
android/app/src/main/kotlin/ru/akokos/ignis_app/
|
||||
├── MainActivity.kt
|
||||
├── GeofenceAutomationManager.kt
|
||||
├── GeofenceBroadcastReceiver.kt
|
||||
├── GeofenceExitWorker.kt
|
||||
└── GeofenceRestoreReceiver.kt
|
||||
```
|
||||
|
||||
## Сборка
|
||||
## Запуск
|
||||
|
||||
```bash
|
||||
# Зависимости
|
||||
flutter pub get
|
||||
|
||||
# Debug-запуск
|
||||
flutter run
|
||||
|
||||
# Release APK (с пробросом build info)
|
||||
flutter build apk --release --dart-define=IGNIS_BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" --dart-define=IGNIS_GIT_SHA="$(git rev-parse --short HEAD)"
|
||||
```
|
||||
|
||||
APK: `build/app/outputs/flutter-apk/app-release.apk`
|
||||
## Release APK
|
||||
|
||||
> Сейчас release APK подписывается debug-ключом из Flutter-шаблона. Для личной установки на телефон этого достаточно, для настоящего релиза подпись нужно заменить.
|
||||
```bash
|
||||
flutter build apk --release \
|
||||
--dart-define=IGNIS_BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
|
||||
--dart-define=IGNIS_GIT_SHA="$(git rev-parse --short HEAD)"
|
||||
```
|
||||
|
||||
Артефакт:
|
||||
|
||||
```text
|
||||
build/app/outputs/flutter-apk/app-release.apk
|
||||
```
|
||||
|
||||
## Проверки
|
||||
|
||||
@@ -106,40 +88,28 @@ flutter analyze
|
||||
flutter test
|
||||
```
|
||||
|
||||
Текущий baseline зелёный: `flutter analyze`, `flutter test` и release APK сборка проходят штатно.
|
||||
|
||||
Дополнительно тестами уже прикрыты:
|
||||
- typed parsing/load-state для основных backend-ответов;
|
||||
- geofence distance/runtime логика;
|
||||
- чистая логика форм расписаний и групп;
|
||||
- provider-мутаторы для расписаний, таймера 4h и API-ключей;
|
||||
- widget-сценарии форм домов, групп, расписаний и API-ключей;
|
||||
- widget-сценарии `RemoteScreen`, `GroupCard` и error/retry-потоков.
|
||||
|
||||
Сейчас baseline клиента закрывается примерно `60` тестами и уже ловит regressions не только в helper-логике, но и в основных пользовательских сценариях.
|
||||
Сейчас тестами прикрыты:
|
||||
- parsing и load-state основных backend-ответов;
|
||||
- сериализация `HomeConfig` и geofence radius;
|
||||
- синхронизация активного дома с geofence automation;
|
||||
- form logic для домов, групп и расписаний;
|
||||
- provider-мутаторы расписаний, API-ключей и group control;
|
||||
- widget-сценарии форм, `GroupCard` и error/retry потоков.
|
||||
|
||||
## Настройка
|
||||
|
||||
При первом запуске приложение попросит добавить "дом" -- указать адрес сервера Ignis и API-ключ. После этого откроется пульт управления группами.
|
||||
1. Добавить дом: адрес сервера Ignis и API-ключ.
|
||||
2. При необходимости задать координаты дома.
|
||||
3. Включить "выключать свет при уходе".
|
||||
4. Выдать Android-разрешения на геолокацию, включая background location.
|
||||
|
||||
Если задать координаты дома, экран домов начнёт показывать расстояние до активного дома. Если дополнительно включить автовыключение при уходе и выдать Android фоновые разрешения на геолокацию и уведомления, приложение сможет в фоне выключать свет при удалении от текущего активного дома.
|
||||
API-ключи хранятся отдельно от списка домов в `flutter_secure_storage`. Старые ключи из `SharedPreferences` мигрируются автоматически.
|
||||
|
||||
Для добавления второго дома: кнопка "домик" в левом верхнем углу пульта -> экран домов -> кнопка "+".
|
||||
## Ограничения
|
||||
|
||||
API-ключи хранятся отдельно от конфигурации домов в `flutter_secure_storage`. Старые ключи из `SharedPreferences` мигрируются автоматически.
|
||||
|
||||
## API
|
||||
|
||||
Приложение работает с [Ignis Core API](https://git.akokos.ru/artem.kokos/ignis-core) -- self-hosted бэкенд на FastAPI (контракт OpenAPI 3.1.0).
|
||||
Авторизация происходит через заголовок `X-API-Key`.
|
||||
Доменный слой на стороне клиента полностью типизирован.
|
||||
|
||||
## Текущие ограничения
|
||||
|
||||
- Целевая платформа сейчас Android.
|
||||
- Release APK пока подписывается debug-ключом из Flutter-шаблона.
|
||||
- Build info в APK показывает дату сборки и короткий git hash текущего `HEAD`. Если сборка делается поверх незакоммиченного рабочего дерева, hash будет от последнего коммита, а не от локальных незакоммиченных изменений.
|
||||
- Android-specific поведение реального background execution, уведомлений, runtime permissions и OEM battery restrictions пока подтверждается в основном ручными проверками на устройстве, а не automated integration-тестами.
|
||||
- целевая платформа сейчас Android;
|
||||
- реальное поведение background execution, geofence delivery и OEM battery restrictions подтверждается в основном ручными проверками на устройстве;
|
||||
- force-stop приложения со стороны Android может ломать автоподъём фоновой логики до следующего ручного запуска.
|
||||
|
||||
## Лицензия
|
||||
|
||||
|
||||
Reference in New Issue
Block a user