mymuseum-visitapp/lib/Services/Glasses/engines/impl/home_assistant_stt_engine.dart

104 lines
3.1 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:mymuseum_visitapp/Services/Glasses/engines/stt_engine.dart';
/// STT via faster-whisper hébergé sur Home Assistant.
///
/// POST http://{haUrl}/api/stt/{providerId}
/// Header : Authorization: Bearer {token}
/// Body : bytes audio WAV 16kHz mono
class HomeAssistantSttEngine implements SttEngine {
final String haUrl;
final String haToken;
final String providerId;
final FlutterSoundRecorder _recorder = FlutterSoundRecorder();
bool _initialized = false;
HomeAssistantSttEngine({
required this.haUrl,
required this.haToken,
this.providerId = 'faster_whisper',
});
Future<void> _ensureInitialized() async {
if (_initialized) return;
await Permission.microphone.request();
await _recorder.openRecorder();
_initialized = true;
}
@override
Future<String> transcribeOnce({
required String languageCode,
Duration timeout = const Duration(seconds: 8),
}) async {
await _ensureInitialized();
final dir = await getTemporaryDirectory();
final path = '${dir.path}/ha_stt_${DateTime.now().millisecondsSinceEpoch}.wav';
try {
await _recorder.startRecorder(
toFile: path,
codec: Codec.pcm16WAV,
sampleRate: 16000,
numChannels: 1,
);
await _waitForSilenceOrTimeout(timeout);
await _recorder.stopRecorder();
return await _transcribe(path, languageCode);
} catch (e) {
debugPrint('[HomeAssistantSttEngine] error: $e');
try { await _recorder.stopRecorder(); } catch (_) {}
return '';
} finally {
try { File(path).deleteSync(); } catch (_) {}
}
}
@override
Future<void> cancel() async {
try { await _recorder.stopRecorder(); } catch (_) {}
}
Future<void> _waitForSilenceOrTimeout(Duration timeout) async {
// Attend la durée complète — l'utilisateur a le temps de parler
// flutter_sound ne fournit pas d'accès simple au niveau audio en temps réel
await Future.delayed(timeout);
}
Future<String> _transcribe(String wavPath, String languageCode) async {
final bytes = await File(wavPath).readAsBytes();
final lang = languageCode.split('-').first;
final response = await http.post(
Uri.parse('$haUrl/api/stt/$providerId'),
headers: {
'Authorization': 'Bearer $haToken',
'Content-Type': 'audio/wav',
'X-Speech-Content': 'language=$lang',
},
body: bytes,
).timeout(const Duration(seconds: 10));
if (response.statusCode != 200) {
debugPrint('[HomeAssistantSttEngine] HTTP ${response.statusCode}: ${response.body}');
return '';
}
final json = jsonDecode(response.body) as Map<String, dynamic>;
final text = (json['text'] as String? ?? '').trim();
debugPrint('[HomeAssistantSttEngine] "$text"');
return text;
}
}