import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../core/api_client.dart'; import '../core/api_service.dart'; import '../models/device.dart'; import '../widgets/status_badge.dart'; class DeviceDetailScreen extends StatefulWidget { const DeviceDetailScreen({super.key, required this.deviceId}); final int deviceId; @override State createState() => _DeviceDetailScreenState(); } class _DeviceDetailScreenState extends State { late Future _future; bool _isSendingCommand = false; @override void initState() { super.initState(); _future = _load(); } Future _load() => context.read().device(widget.deviceId); Future _refresh() async { final future = _load(); setState(() => _future = future); await future; } Future _runCommand(Future Function() command) async { setState(() => _isSendingCommand = true); try { final message = await command(); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); await _refresh(); } on ApiException catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.message))); } finally { if (mounted) setState(() => _isSendingCommand = false); } } Future _promptSetLevel(int deviceId) async { final apiService = context.read(); double level = 50; final confirmed = await showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setDialogState) => AlertDialog( title: const Text('Установить уровень'), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text('${level.round()}'), Slider( value: level, min: 0, max: 100, divisions: 100, onChanged: (value) => setDialogState(() => level = value), ), ], ), actions: [ TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Отмена')), FilledButton(onPressed: () => Navigator.pop(context, true), child: const Text('Отправить')), ], ), ), ); if (confirmed == true) { await _runCommand(() => apiService.setLevel(deviceId, level)); } } @override Widget build(BuildContext context) { final apiService = context.read(); return Scaffold( appBar: AppBar(title: const Text('Устройство')), body: RefreshIndicator( onRefresh: _refresh, child: FutureBuilder( future: _future, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return ListView( physics: const AlwaysScrollableScrollPhysics(), children: [ Padding( padding: const EdgeInsets.all(24), child: Text('${snapshot.error}', textAlign: TextAlign.center), ), ], ); } final detail = snapshot.data!; final device = detail.device; final capabilities = device.deviceType?.capabilities ?? const []; return ListView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(16), children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text(device.name, style: Theme.of(context).textTheme.headlineSmall), ), StatusBadge(status: device.status), ], ), const SizedBox(height: 4), Text('${device.zone?.name ?? '—'} · ${device.deviceType?.code ?? device.protocol}'), if (detail.lastSeen != null) Padding( padding: const EdgeInsets.only(top: 4), child: Text('Последний сигнал: ${detail.lastSeen}'), ), const SizedBox(height: 24), if (capabilities.contains('turn_on') || capabilities.contains('turn_off')) Row( children: [ if (capabilities.contains('turn_on')) Expanded( child: FilledButton( onPressed: _isSendingCommand ? null : () => _runCommand(() => apiService.turnOn(device.id)), child: const Text('Включить'), ), ), if (capabilities.contains('turn_on') && capabilities.contains('turn_off')) const SizedBox(width: 12), if (capabilities.contains('turn_off')) Expanded( child: OutlinedButton( onPressed: _isSendingCommand ? null : () => _runCommand(() => apiService.turnOff(device.id)), child: const Text('Выключить'), ), ), ], ), if (capabilities.contains('set_level')) ...[ const SizedBox(height: 12), OutlinedButton( onPressed: _isSendingCommand ? null : () => _promptSetLevel(device.id), child: const Text('Установить уровень'), ), ], if (detail.reportedState.isNotEmpty) ...[ const SizedBox(height: 24), Text('Сообщённое состояние', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), _KeyValueTable(data: detail.reportedState), ], if (detail.telemetry.isNotEmpty) ...[ const SizedBox(height: 24), Text('История показаний', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), ...detail.telemetry.map( (reading) => ListTile( contentPadding: EdgeInsets.zero, title: Text('${reading.sensorType}: ${reading.value}'), subtitle: Text(reading.recordedAt), dense: true, ), ), ], ], ); }, ), ), ); } } class _KeyValueTable extends StatelessWidget { const _KeyValueTable({required this.data}); final Map data; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: data.entries .map((entry) => Padding( padding: const EdgeInsets.symmetric(vertical: 2), child: Text('${entry.key}: ${entry.value}'), )) .toList(), ); } }