136 lines
5.6 KiB
Dart
136 lines
5.6 KiB
Dart
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<void> validateCredentials(String baseUrl, String apiKey) async {
|
||
final probe = IgnisApi()..init(baseUrl, apiKey);
|
||
try {
|
||
await probe.getAuthMe();
|
||
} finally {
|
||
probe.dioInstance.close();
|
||
}
|
||
}
|
||
|
||
// ─── Авторизация ───────────────────────────────────────────
|
||
|
||
/// Проверка текущего ключа: возвращает {is_admin, name}
|
||
Future<Response> getAuthMe() => _dio.get('/auth/me');
|
||
|
||
// ─── Устройства и группы ───────────────────────────────────
|
||
|
||
/// Все устройства (лампы)
|
||
Future<Response> getDevices() => _dio.get('/devices');
|
||
|
||
/// Все группы
|
||
Future<Response> getGroups() => _dio.get('/devices/groups');
|
||
|
||
/// Все доступные сцены
|
||
Future<Response> getScenes() => _dio.get('/devices/scenes');
|
||
|
||
/// Создать группу
|
||
Future<Response> createGroup(String id, String name, List<String> macs) =>
|
||
_dio.post(
|
||
'/devices/groups',
|
||
data: {'id': id, 'name': name, 'macs': macs},
|
||
);
|
||
|
||
/// Удалить группу
|
||
Future<Response> deleteGroup(String groupId) =>
|
||
_dio.delete('/devices/groups/$groupId');
|
||
|
||
/// Пересканировать сеть (найти новые лампы)
|
||
Future<Response> rescanNetwork() => _dio.post('/devices/rescan');
|
||
|
||
// ─── Управление ────────────────────────────────────────────
|
||
|
||
/// Управление группой: state, brightness, temp, scene, r/g/b
|
||
Future<Response> controlGroup(String id, Map<String, dynamic> params) =>
|
||
_dio.post('/control/group/$id', data: params);
|
||
|
||
/// Управление одной лампой
|
||
Future<Response> controlDevice(String id, Map<String, dynamic> params) =>
|
||
_dio.post('/control/device/$id', data: params);
|
||
|
||
/// Мигнуть лампой (для идентификации)
|
||
Future<Response> blinkDevice(String id) =>
|
||
_dio.post('/control/device/$id/blink');
|
||
|
||
/// Статус группы (реальный опрос ламп)
|
||
Future<Response> getGroupStatus(String id) =>
|
||
_dio.get('/control/group/$id/status');
|
||
|
||
/// Статус одной лампы
|
||
Future<Response> getDeviceStatus(String id) =>
|
||
_dio.get('/control/device/$id/status');
|
||
|
||
// ─── Расписания ────────────────────────────────────────────
|
||
|
||
/// Одноразовое расписание (таймер)
|
||
Future<Response> scheduleOnce(Map<String, dynamic> params) =>
|
||
_dio.post('/schedules/once', data: params);
|
||
|
||
/// Cron-расписание (повторяющееся)
|
||
Future<Response> scheduleCron(Map<String, dynamic> params) =>
|
||
_dio.post('/schedules/cron', data: params);
|
||
|
||
/// Все активные задачи расписания
|
||
Future<Response> getTasks() => _dio.get('/schedules/tasks');
|
||
|
||
/// Отменить задачу расписания
|
||
Future<Response> cancelTask(String jobId) => _dio.delete('/schedules/$jobId');
|
||
|
||
// ─── API-ключи ─────────────────────────────────────────────
|
||
|
||
/// Список всех гостевых ключей
|
||
Future<Response> getApiKeys() => _dio.get('/api-keys');
|
||
|
||
/// Создать гостевой ключ
|
||
Future<Response> createApiKey(String name, {bool isAdmin = false}) => _dio
|
||
.post('/api-keys', queryParameters: {'name': name, 'is_admin': isAdmin});
|
||
|
||
/// Отозвать ключ (body: {key: ...})
|
||
Future<Response> revokeApiKey(String key) =>
|
||
_dio.post('/api-keys/revoke', data: {'key': key});
|
||
|
||
/// Активировать ключ (body: {key: ...})
|
||
Future<Response> activateApiKey(String key) =>
|
||
_dio.post('/api-keys/activate', data: {'key': key});
|
||
|
||
// ─── Статистика ────────────────────────────────────────────
|
||
|
||
/// Сводная статистика за N дней
|
||
Future<Response> getStatsSummary({int days = 7}) =>
|
||
_dio.get('/stats/summary', queryParameters: {'days': days});
|
||
|
||
/// Лог последних N событий
|
||
Future<Response> getStatsLog({int limit = 100}) =>
|
||
_dio.get('/stats/log', queryParameters: {'limit': limit});
|
||
}
|