import 'dart:convert'; import 'package:http/http.dart' as http; import '../models/device.dart'; import '../models/user_info.dart'; import '../models/static_talkgroup.dart'; import '../models/device_profile.dart'; class BrandmeisterError implements Exception { final String message; final int? statusCode; BrandmeisterError(this.message, {this.statusCode}); @override String toString() { if (statusCode != null) { return 'BrandmeisterError (HTTP $statusCode): $message'; } return 'BrandmeisterError: $message'; } } class BrandmeisterClient { static const String baseUrl = 'https://api.brandmeister.network/v2'; final String apiToken; BrandmeisterClient({required this.apiToken}); Map get _headers => { 'Authorization': 'Bearer $apiToken', 'Content-Type': 'application/json', }; Future _handleResponse( http.Response response, T Function(dynamic) parser, ) async { if (response.statusCode >= 200 && response.statusCode < 300) { try { final dynamic decoded = jsonDecode(response.body); return parser(decoded); } catch (e) { throw BrandmeisterError('Failed to decode response: $e'); } } else { throw BrandmeisterError( 'HTTP request failed: ${response.body}', statusCode: response.statusCode, ); } } // User Endpoints Future whoami() async { final response = await http.post( Uri.parse('$baseUrl/user/whoAmI'), headers: _headers, ); return _handleResponse(response, (json) => UserInfo.fromJson(json)); } Future getUser(int id) async { final response = await http.get( Uri.parse('$baseUrl/user/byID/$id'), headers: _headers, ); return _handleResponse(response, (json) => UserInfo.fromJson(json)); } Future> getUsersByCallsign(String callsign) async { final response = await http.get( Uri.parse('$baseUrl/user/byCall/$callsign'), headers: _headers, ); return _handleResponse( response, (json) => (json as List).map((e) => UserInfo.fromJson(e)).toList(), ); } // Device Endpoints Future getDevice(int dmrId) async { final response = await http.get( Uri.parse('$baseUrl/device/$dmrId'), headers: _headers, ); return _handleResponse(response, (json) => Device.fromJson(json)); } Future getDeviceProfile(int dmrId) async { final response = await http.get( Uri.parse('$baseUrl/device/$dmrId/profile'), headers: _headers, ); return _handleResponse(response, (json) => DeviceProfile.fromJson(json)); } Future> getDevicesByCallsign(String callsign) async { final response = await http.get( Uri.parse('$baseUrl/device/byCall?callsign=$callsign'), headers: _headers, ); return _handleResponse( response, (json) => (json as List).map((e) => Device.fromJson(e)).toList(), ); } Future> getDevicesByMaster(int masterId) async { final response = await http.get( Uri.parse('$baseUrl/device/byMaster/$masterId'), headers: _headers, ); return _handleResponse( response, (json) => (json as List).map((e) => Device.fromJson(e)).toList(), ); } Future> getDeviceTalkgroups(int dmrId) async { final response = await http.get( Uri.parse('$baseUrl/device/$dmrId/talkgroup'), headers: _headers, ); return _handleResponse( response, (json) => (json as List).map((e) => StaticTalkgroup.fromJson(e)).toList(), ); } // Talkgroup Management Future addTalkgroup({ required int dmrId, required int talkgroupId, required int slot, }) async { final response = await http.post( Uri.parse('$baseUrl/device/$dmrId/talkgroup'), headers: _headers, body: jsonEncode({ 'slot': slot, 'group': talkgroupId, }), ); if (response.statusCode < 200 || response.statusCode >= 300) { throw BrandmeisterError( 'Failed to add talkgroup: ${response.body}', statusCode: response.statusCode, ); } } Future removeTalkgroup({ required int dmrId, required int talkgroupId, required int slot, }) async { final response = await http.delete( Uri.parse('$baseUrl/device/$dmrId/talkgroup/$slot/$talkgroupId'), headers: _headers, ); if (response.statusCode < 200 || response.statusCode >= 300) { throw BrandmeisterError( 'Failed to remove talkgroup: ${response.body}', statusCode: response.statusCode, ); } } // Talkgroup Endpoints Future> getTalkgroups() async { final response = await http.get( Uri.parse('$baseUrl/talkgroup'), headers: _headers, ); return _handleResponse( response, (json) => (json as Map).map( (key, value) => MapEntry(key, value.toString()), ), ); } }