Files
smart-serow/pi/ui/lib/screens/overheat_screen.dart
Mikkeli Matlock c7edc30b79 multiple updates
- colour theme implemented. ThemeService based global switching for future light detection triggers
- test flipflop service for various fun
- navigator widget class with state switching
2026-01-26 00:20:52 +09:00

155 lines
4.1 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import '../services/config_service.dart';
import '../services/pi_io.dart';
import '../theme/app_theme.dart';
/// Overheat warning screen with shutdown countdown
///
/// Shows current temp, threshold, and countdown to poweroff.
/// When countdown hits zero, executes system shutdown.
class OverheatScreen extends StatefulWidget {
const OverheatScreen({super.key});
@override
State<OverheatScreen> createState() => _OverheatScreenState();
}
class _OverheatScreenState extends State<OverheatScreen> {
Timer? _countdownTimer;
Timer? _tempRefreshTimer;
late int _secondsRemaining;
double? _currentTemp;
@override
void initState() {
super.initState();
_secondsRemaining = ConfigService.instance.shutdownDelay.inSeconds;
_currentTemp = PiIO.instance.getTemperature();
// Countdown timer - ticks every second
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (_) {
setState(() {
_secondsRemaining--;
});
if (_secondsRemaining <= 0) {
_countdownTimer?.cancel();
_executeShutdown();
}
});
// Keep temp display updated
_tempRefreshTimer = Timer.periodic(const Duration(milliseconds: 500), (_) {
setState(() {
_currentTemp = PiIO.instance.getTemperature();
});
});
}
@override
void dispose() {
_countdownTimer?.cancel();
_tempRefreshTimer?.cancel();
super.dispose();
}
Future<void> _executeShutdown() async {
// Try shutdown commands in order of preference
// Requires passwordless sudo for 'shutdown' command (see sudoers note below)
final commands = [
['sudo', 'shutdown', '-h', 'now'],
['sudo', 'poweroff'],
['systemctl', 'poweroff'], // Might work with polkit
];
for (final cmd in commands) {
try {
final result = await Process.run(cmd.first, cmd.skip(1).toList());
if (result.exitCode == 0) return; // Success
} catch (e) {
// Command not found or other error, try next
}
}
// All failed - we're probably not on Linux or no permissions
// Pi should have passwordless sudo configured:
// echo "pi ALL=(ALL) NOPASSWD: /sbin/shutdown" | sudo tee /etc/sudoers.d/shutdown
}
@override
Widget build(BuildContext context) {
final theme = AppTheme.of(context);
final threshold = ConfigService.instance.overheatThreshold;
return Scaffold(
backgroundColor: theme.background,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Warning icon
Icon(
Icons.warning_amber_rounded,
size: 100,
color: theme.highlight,
),
const SizedBox(height: 16),
// OVERHEATING text
Text(
'OVERHEATING',
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.bold,
color: theme.highlight,
letterSpacing: 4,
),
),
const SizedBox(height: 48),
// Current temperature
Text(
_currentTemp != null ? '${_currentTemp!.toStringAsFixed(1)}°C' : '',
style: TextStyle(
fontSize: 120,
fontWeight: FontWeight.w200,
color: theme.foreground,
height: 1,
),
),
const SizedBox(height: 8),
// Threshold info
Text(
'Threshold: ${threshold.toStringAsFixed(0)}°C',
style: TextStyle(
fontSize: 24,
color: theme.subdued,
),
),
const SizedBox(height: 48),
// Countdown
Text(
'Shutdown in $_secondsRemaining s',
style: TextStyle(
fontSize: 32,
color: theme.highlight,
fontWeight: FontWeight.w500,
),
),
],
),
),
);
}
}