import 'package:dio/dio.dart'; /// HTTP-клиент для одного сервера Ignis. /// Покрывает все эндпоинты из openapi.json. class IgnisApi { IgnisApi({Dio? dio}) : _dio = dio ?? Dio(); final Dio _dio; Dio get dioInstance => _dio; static String normalizeBaseUrl(String baseUrl) { var url = baseUrl.trim(); final lowerUrl = url.toLowerCase(); if (!lowerUrl.startsWith('http://') && !lowerUrl.startsWith('https://')) { url = 'https://$url'; } while (url.endsWith('/')) { url = url.substring(0, url.length - 1); } return url; } /// Инициализация базового URL и API-ключа void init(String baseUrl, String apiKey) { _dio.options.baseUrl = normalizeBaseUrl(baseUrl); _dio.options.headers['X-API-Key'] = apiKey; // Бэкенд WiZ ламп тормозит -- даём запас _dio.options.connectTimeout = const Duration(seconds: 15); _dio.options.receiveTimeout = const Duration(seconds: 15); } Future validateCredentials(String baseUrl, String apiKey) async { final probe = IgnisApi()..init(baseUrl, apiKey); try { await probe.getAuthMe(); } finally { probe.dioInstance.close(); } } // ─── Авторизация ─────────────────────────────────────────── /// Проверка текущего ключа: возвращает {is_admin, name} Future getAuthMe() => _dio.get('/auth/me'); // ─── Устройства и группы ─────────────────────────────────── /// Все устройства (лампы) Future getDevices() => _dio.get('/devices'); /// Все группы Future getGroups() => _dio.get('/devices/groups'); /// Все доступные сцены Future getScenes() => _dio.get('/devices/scenes'); /// Создать группу Future createGroup(String id, String name, List macs) => _dio.post( '/devices/groups', data: {'id': id, 'name': name, 'macs': macs}, ); /// Удалить группу Future deleteGroup(String groupId) => _dio.delete('/devices/groups/$groupId'); /// Пересканировать сеть (найти новые лампы) Future rescanNetwork() => _dio.post('/devices/rescan'); // ─── Управление ──────────────────────────────────────────── /// Управление группой: state, brightness, temp, scene, r/g/b Future controlGroup(String id, Map params) => _dio.post('/control/group/$id', data: params); /// Управление одной лампой Future controlDevice(String id, Map params) => _dio.post('/control/device/$id', data: params); /// Мигнуть лампой (для идентификации) Future blinkDevice(String id) => _dio.post('/control/device/$id/blink'); /// Статус группы (реальный опрос ламп) Future getGroupStatus(String id) => _dio.get('/control/group/$id/status'); /// Статус одной лампы Future getDeviceStatus(String id) => _dio.get('/control/device/$id/status'); // ─── Расписания ──────────────────────────────────────────── /// Одноразовое расписание (таймер) Future scheduleOnce(Map params) => _dio.post('/schedules/once', data: params); /// Cron-расписание (повторяющееся) Future scheduleCron(Map params) => _dio.post('/schedules/cron', data: params); /// Все активные задачи расписания Future getTasks() => _dio.get('/schedules/tasks'); /// Отменить задачу расписания Future cancelTask(String jobId) => _dio.delete('/schedules/$jobId'); // ─── API-ключи ───────────────────────────────────────────── /// Список всех гостевых ключей Future getApiKeys() => _dio.get('/api-keys'); /// Создать гостевой ключ Future createApiKey(String name, {bool isAdmin = false}) => _dio .post('/api-keys', queryParameters: {'name': name, 'is_admin': isAdmin}); /// Отозвать ключ (body: {key: ...}) Future revokeApiKey(String key) => _dio.post('/api-keys/revoke', data: {'key': key}); /// Активировать ключ (body: {key: ...}) Future activateApiKey(String key) => _dio.post('/api-keys/activate', data: {'key': key}); // ─── Статистика ──────────────────────────────────────────── /// Сводная статистика за N дней Future getStatsSummary({int days = 7}) => _dio.get('/stats/summary', queryParameters: {'days': days}); /// Лог последних N событий Future getStatsLog({int limit = 100}) => _dio.get('/stats/log', queryParameters: {'limit': limit}); }