99 lines
3.7 KiB
Dart
99 lines
3.7 KiB
Dart
|
|
import 'dart:convert';
|
||
|
|
import 'package:http/http.dart' as http;
|
||
|
|
import '../models/busylight_color.dart';
|
||
|
|
import '../models/busylight_status.dart';
|
||
|
|
|
||
|
|
class BusylightException implements Exception {
|
||
|
|
final String message;
|
||
|
|
const BusylightException(this.message);
|
||
|
|
@override
|
||
|
|
String toString() => 'BusylightException: $message';
|
||
|
|
}
|
||
|
|
|
||
|
|
class BusylightService {
|
||
|
|
final String baseUrl;
|
||
|
|
final Duration timeout;
|
||
|
|
|
||
|
|
BusylightService({
|
||
|
|
required this.baseUrl,
|
||
|
|
this.timeout = const Duration(seconds: 5),
|
||
|
|
});
|
||
|
|
|
||
|
|
Uri _uri(String path) => Uri.parse('$baseUrl$path');
|
||
|
|
|
||
|
|
Future<Map<String, dynamic>> _get(String path) async {
|
||
|
|
try {
|
||
|
|
final res = await http.get(_uri(path)).timeout(timeout);
|
||
|
|
_checkStatus(res);
|
||
|
|
return jsonDecode(res.body) as Map<String, dynamic>;
|
||
|
|
} on BusylightException {
|
||
|
|
rethrow;
|
||
|
|
} catch (e) {
|
||
|
|
throw BusylightException('Network error: $e');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<Map<String, dynamic>> _post(String path, [Map<String, dynamic>? body]) async {
|
||
|
|
try {
|
||
|
|
final res = await http
|
||
|
|
.post(
|
||
|
|
_uri(path),
|
||
|
|
headers: {'Content-Type': 'application/json'},
|
||
|
|
body: body != null ? jsonEncode(body) : null,
|
||
|
|
)
|
||
|
|
.timeout(timeout);
|
||
|
|
_checkStatus(res);
|
||
|
|
return jsonDecode(res.body) as Map<String, dynamic>;
|
||
|
|
} on BusylightException {
|
||
|
|
rethrow;
|
||
|
|
} catch (e) {
|
||
|
|
throw BusylightException('Network error: $e');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void _checkStatus(http.Response res) {
|
||
|
|
if (res.statusCode < 200 || res.statusCode >= 300) {
|
||
|
|
throw BusylightException('HTTP ${res.statusCode}: ${res.body}');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Status ──────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
Future<BusylightStatus> getStatus() async {
|
||
|
|
final json = await _get('/api/status');
|
||
|
|
return BusylightStatus.fromString(json['status'] as String);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<BusylightStatus> setStatus(BusylightStatus status) async {
|
||
|
|
final json = await _post(status.apiPath);
|
||
|
|
return BusylightStatus.fromString(json['status'] as String);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<BusylightStatus> turnOn() => setStatus(BusylightStatus.on);
|
||
|
|
Future<BusylightStatus> turnOff() => setStatus(BusylightStatus.off);
|
||
|
|
Future<BusylightStatus> setAvailable() => setStatus(BusylightStatus.available);
|
||
|
|
Future<BusylightStatus> setAway() => setStatus(BusylightStatus.away);
|
||
|
|
Future<BusylightStatus> setBusy() => setStatus(BusylightStatus.busy);
|
||
|
|
|
||
|
|
// ── Color ───────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
Future<BusylightColor> getColor() async {
|
||
|
|
final json = await _get('/api/color');
|
||
|
|
return BusylightColor.fromJson(json);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setColor(BusylightColor color) async {
|
||
|
|
await _post('/api/color', color.toJson());
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Brightness ──────────────────────────────────────────────────────────────
|
||
|
|
// Note: brightness is read from GET /api/color response, no separate endpoint needed.
|
||
|
|
|
||
|
|
Future<void> setBrightness(double brightness) async {
|
||
|
|
await _post('/api/brightness', {'brightness': brightness.clamp(0.0, 1.0)});
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Debug ───────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
Future<Map<String, dynamic>> getDebug() => _get('/api/debug');
|
||
|
|
}
|