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 _ensureInitialized() async { if (_initialized) return; await Permission.microphone.request(); await _recorder.openRecorder(); _initialized = true; } @override Future 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 cancel() async { try { await _recorder.stopRecorder(); } catch (_) {} } Future _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 _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; final text = (json['text'] as String? ?? '').trim(); debugPrint('[HomeAssistantSttEngine] "$text"'); return text; } }