import 'dart:convert'; import 'package:mqtt_client/mqtt_client.dart'; import 'package:mqtt_client/mqtt_server_client.dart'; import 'package:flutter/foundation.dart'; class MqttService extends ChangeNotifier { MqttServerClient? client; // Data sensor String _suhu = "0"; String _kelembapan = "0"; String _intensitas = "0"; bool _isKipas1On = false; bool _isKipas2On = false; String _mode = "otomatis"; String _timestamp = "-"; // Batas sensor double _maxSuhu = 45.0; double _maxRh = 70.0; // Getter sensor String get suhu => _suhu; String get kelembapan => _kelembapan; String get intensitas => _intensitas; bool get isKipas1On => _isKipas1On; bool get isKipas2On => _isKipas2On; String get mode => _mode; String get timestamp => _timestamp; // Getter batas double get maxSuhu => _maxSuhu; double get maxRh => _maxRh; Function(String, bool)? onConnectionResult; void setLimits(double suhuMax, double rhMax) { _maxSuhu = suhuMax; _maxRh = rhMax; notifyListeners(); } Future connect() async { final String clientId = 'flutter_kopi_${DateTime.now().millisecondsSinceEpoch % 10000}'; client = MqttServerClient.withPort( 's1fd277a.ala.asia-southeast1.emqxsl.com', // EMQX host baru clientId, 8883, ); client!.secure = true; client!.setProtocolV311(); client!.onBadCertificate = (dynamic cert) => true; client!.keepAlivePeriod = 20; client!.connectTimeoutPeriod = 10000; client!.onDisconnected = onDisconnected; client!.onConnected = onConnected; client!.logging(on: kDebugMode); final connMess = MqttConnectMessage() .withClientIdentifier(clientId) .authenticateAs('kopi_user', 'Kopikopi1') .startClean(); client!.connectionMessage = connMess; try { await client!.connect(); } catch (e) { debugPrint('MQTT Exception: $e'); onConnectionResult?.call('Koneksi Gagal: $e', false); client!.disconnect(); return false; } if (client?.connectionStatus?.state == MqttConnectionState.connected) { debugPrint('MQTT: Terhubung!'); onConnectionResult?.call('✅ Terhubung ke Server Kopi', true); _subscribeToTopics(); notifyListeners(); return true; } else { onConnectionResult?.call('❌ Koneksi Gagal', false); notifyListeners(); return false; } } void _subscribeToTopics() { client!.subscribe("kopi/sensor", MqttQos.atMostOnce); client!.subscribe("kopi/relay/kipas1", MqttQos.atMostOnce); client!.subscribe("kopi/relay/kipas2", MqttQos.atMostOnce); client!.subscribe("kopi/relay/mode", MqttQos.atMostOnce); client!.updates!.listen((List>? c) { if (c == null || c.isEmpty) return; final recMess = c[0].payload as MqttPublishMessage; final pt = MqttPublishPayload.bytesToStringAsString( recMess.payload.message); final topic = c[0].topic; if (topic == "kopi/sensor") { try { final data = jsonDecode(pt); _suhu = data['suhu'].toString(); _kelembapan = data['kelembapan'].toString(); _intensitas = data['intensitas'].toString(); _isKipas1On = data['kipas1'] == 'ON'; _isKipas2On = data['kipas2'] == 'ON'; _mode = data['mode'] ?? 'otomatis'; _timestamp = data['timestamp'] ?? '-'; } catch (e) { debugPrint('MQTT parse error: $e'); } } if (topic == "kopi/relay/kipas1") _isKipas1On = (pt == "ON"); if (topic == "kopi/relay/kipas2") _isKipas2On = (pt == "ON"); if (topic == "kopi/relay/mode") _mode = pt; notifyListeners(); }); } // Kirim perintah kipas manual void perintahKipas(String nomorKipas, bool nyala) { final payload = jsonEncode({ "kipas": nomorKipas, "status": nyala ? "ON" : "OFF", }); _publish("kopi/relay/manual", payload); } // Ganti mode otomatis/manual void gantiMode(String mode) { _publish("kopi/relay/mode", mode); } // Kirim interval foto dalam menit void setIntervalFoto(int menit) { _publish("kopi/setting/interval", menit.toString()); } // Trigger foto manual void triggerFoto() { _publish("kopi/trigger/foto", "AMBIL"); } // Kirim batas sensor ke ESP32 dan Node-RED void setBatasSensor( double suhuMin, double suhuMax, double rhMin, double rhMax) { final payload = jsonEncode({ "suhu_min": suhuMin, "suhu_max": suhuMax, "rh_min": rhMin, "rh_max": rhMax, }); _publish("kopi/setting/batas", payload); } void _publish(String topic, String message) { if (client?.connectionStatus?.state != MqttConnectionState.connected) { debugPrint('MQTT: Tidak terhubung, tidak bisa publish'); return; } final builder = MqttClientPayloadBuilder(); builder.addString(message); client!.publishMessage(topic, MqttQos.atMostOnce, builder.payload!); debugPrint('MQTT publish → $topic : $message'); } void onConnected() { debugPrint('MQTT: onConnected'); notifyListeners(); } void onDisconnected() { debugPrint('MQTT: onDisconnected'); // 🔥 AUTO RECONNECT Future.delayed(const Duration(seconds: 3), () { debugPrint('MQTT: mencoba reconnect...'); connect(); }); notifyListeners(); } void disconnect() { client?.disconnect(); } }