import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:flutter/cupertino.dart'; import 'DataDisplayPage.dart'; import 'SettingsPage.dart'; DatabaseReference _dataListrikRef = FirebaseDatabase.instance.reference().child('data-listrik'); final TextEditingController addTokenController = TextEditingController(); Stream _streamArus = FirebaseDatabase.instance .reference() .child('sensor') .child('current') .onValue .map((event) => event.snapshot.value.toString()); Stream _streamTegangan = FirebaseDatabase.instance .reference() .child('sensor') .child('voltage') .onValue .map((event) => event.snapshot.value.toString()); Stream _streamKwh = FirebaseDatabase.instance .reference() .child('sensor') .child('energy') .onValue .map((event) => event.snapshot.value.toString()); Stream _streamWatt = FirebaseDatabase.instance .reference() .child('sensor') .child('power') .onValue .map((event) => event.snapshot.value.toString()); class Dashboard extends StatefulWidget { @override _DashboardState createState() => _DashboardState(); static void sendMessageToTelegram(String s) {} } class _DashboardState extends State { DatabaseReference _tokenRef = FirebaseDatabase.instance.reference().child('tokens'); DatabaseReference _channelRef = FirebaseDatabase.instance.reference().child('channels'); static Future sendMessageToTelegram(String message) async { final String botToken = '6904712924:AAGEVa4_ejmj-4uKz_nt_i-ceghMPWYW_5M'; final String chatId = '1709517653'; final String url = 'https://api.telegram.org/bot$botToken/sendMessage'; try { await http.post( Uri.parse(url), body: { 'chat_id': chatId, 'text': message, }, ); } catch (e) { print('Error: $e'); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color(0xFF102C57), body: SingleChildScrollView( child: Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ account(context), SizedBox(height: 20), monitoring(), resetButton(), kwhInput(), button(), relayControl(), SizedBox(height: 20), ], ), ), ), ); } void resetDataListrik() { _dataListrikRef.remove().then((_) { print('Data-listrik deleted successfully'); }).catchError((error) { print('Error deleting data-listrik: $error'); }); } void _showSuccessDialog() { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0), ), backgroundColor: Colors.transparent, content: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black, blurRadius: 10.0, offset: Offset(0.0, 10.0), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( "Proses", style: TextStyle( fontSize: 22, fontWeight: FontWeight.w600, ), textAlign: TextAlign.center, ), SizedBox(height: 20), Text( "Token berhasil dimasukkan ke dalam database dan diproses.", style: TextStyle( fontSize: 16.0, ), textAlign: TextAlign.left, ), SizedBox(height: 24), Align( alignment: Alignment.bottomRight, child: TextButton( onPressed: () { Navigator.of(context).pop(); // Tutup dialog }, child: Text( "OK", style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ), ), ], ), ), ); }, ); } Future _checkIfTokenExists(String token) async { DatabaseEvent event = await _tokenRef.child(token).once(); DataSnapshot snapshot = event.snapshot; return snapshot.value != null; } void _addTokenToDatabase(String token) { String tokenWithoutSpaces = token.replaceAll(' ', ''); if (tokenWithoutSpaces.isEmpty) { print('Token harus diisi'); return; } try { // Menyimpan token sebagai nilai di bawah node 'tokens' _tokenRef.child('tokens').set(tokenWithoutSpaces).then((_) { print('Token berhasil diperbarui: $tokenWithoutSpaces'); sendMessageToTelegram( 'Token berhasil diperbarui dan diproses: $tokenWithoutSpaces'); sendMessageToTelegram( 'Tunggu beberapa saat untuk memvalidasi token dan Silakan kirim kata /photo untuk memvalidasi kwh.'); _showSuccessDialog(); }).catchError((error) { print('Error: $error'); sendMessageToTelegram('Gagal memperbarui token: $error'); }); } catch (e) { print('Error: $e'); sendMessageToTelegram('Gagal memperbarui token: $e'); } } void _updateChannelStatus(String channelName, bool status) { try { _channelRef.update({ channelName: status ? "On" : "Off", }); print( 'Status $channelName berhasil diperbarui: ${status ? "On" : "Off"}'); } catch (e) { print('Error: $e'); } } Widget account(BuildContext context) { return Center( child: Container( margin: EdgeInsets.only(top: 50), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( margin: EdgeInsets.only(left: 20), height: 40, width: 40, decoration: BoxDecoration( shape: BoxShape.circle, image: DecorationImage( image: AssetImage('assets/user.png'), fit: BoxFit.cover, ), ), ), SizedBox(width: 20), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Hai !', style: GoogleFonts.poppins(color: Colors.white), ), Text( 'Nuril Akbar', style: GoogleFonts.poppins( fontSize: 16, color: Colors.white, fontWeight: FontWeight.bold, ), ), ], ), ], ), Row( children: [ IconButton( onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => SettingsPage()), ); }, icon: Icon( Icons.settings, color: Colors.white, size: 30, ), ), IconButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => DataDisplayPage()), ); }, icon: Icon( CupertinoIcons.doc_chart_fill, color: Colors.white, size: 30, ), ), ], ), ], ), ), ); } Widget monitoring() { return Center( child: Container( margin: EdgeInsets.only(top: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Monitoring', style: GoogleFonts.poppins( fontWeight: FontWeight.bold, color: Colors.white, ), ), SizedBox(height: 14), Container( height: 230, width: 330, decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Colors.white, ), child: Container( margin: EdgeInsets.all(10), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildMonitoringBox('Arus', _streamArus), _buildMonitoringBox('Tegangan', _streamTegangan), ], ), SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildMonitoringBox('kWh', _streamKwh), _buildMonitoringBox('Watt', _streamWatt), ], ), ], ), ), ), SizedBox(height: 2), ], ), ), ); } Widget _buildMonitoringBox(String title, Stream stream) { return Container( width: 150, child: Column( children: [ Text(title), SizedBox(height: 8), Container( height: 70, width: double.infinity, decoration: BoxDecoration( color: Colors.amber, borderRadius: BorderRadius.circular(8), ), alignment: Alignment.center, child: StreamBuilder( stream: stream, builder: (context, snapshot) { if (!snapshot.hasData || snapshot.hasError) { return CircularProgressIndicator(); // Placeholder jika data belum dimuat } var value = snapshot.data ?? '0'; return Text( value, style: TextStyle( fontSize: 30, fontWeight: FontWeight.bold, color: Colors.white, ), ); }, ), ), ], ), ); } Widget resetButton() { return Column( children: [ SizedBox(height: 2), Align( alignment: Alignment.center, child: Container( margin: EdgeInsets.symmetric(vertical: 20), height: 54, width: 220, decoration: BoxDecoration( borderRadius: BorderRadius.circular(28), color: Color(0xFFDAC0A3), ), child: TextButton( onPressed: () { resetDataListrik(); // Call the reset function here }, child: Text( 'Reset', style: GoogleFonts.poppins( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 16, ), ), ), ), ), ], ); } Widget kwhInput() { return Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Token KWH', style: GoogleFonts.poppins( fontWeight: FontWeight.bold, color: Colors.white, ), ), SizedBox(height: 12), Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: Colors.white, ), child: Padding( padding: EdgeInsets.all(15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( controller: addTokenController, style: GoogleFonts.poppins( fontWeight: FontWeight.w300, fontSize: 20, ), textAlign: TextAlign.center, inputFormatters: [ LengthLimitingTextInputFormatter(24), FilteringTextInputFormatter.digitsOnly, _TokenInputFormatter(), ], keyboardType: TextInputType.number, decoration: InputDecoration.collapsed( hintText: '', hintStyle: GoogleFonts.poppins( fontWeight: FontWeight.w300, color: Colors.white, ), ), validator: (value) { if (value!.isEmpty) { return 'Token harus diisi'; } if (value.replaceAll(' ', '').length != 20) { return 'Token harus terdiri dari 20 digit'; } return null; // Remove validation message if length is 20 }, ), SizedBox(height: 8), Text( '*Token harus terdiri dari 20 digit', style: TextStyle(color: Colors.red), ), ], ), ), ), ], ), ); } Widget button() { return Padding( padding: EdgeInsets.symmetric(vertical: 20), child: Align( alignment: Alignment.center, child: Container( height: 54, width: 220, decoration: BoxDecoration( borderRadius: BorderRadius.circular(28), color: Color(0xFFDAC0A3), ), child: TextButton( onPressed: () async { String token = addTokenController.text.trim(); if (token.replaceAll(' ', '').length == 20) { _addTokenToDatabase(token); addTokenController .clear(); // Membersihkan input field setelah menambahkan token } else { print('Token harus terdiri dari 20 karakter'); } }, child: Text( 'Input', style: GoogleFonts.poppins( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 16, ), ), ), ), ), ); } Widget relayControl() { return Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Switch Control', style: GoogleFonts.poppins( fontWeight: FontWeight.bold, color: Colors.white, ), ), SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Column( children: [ Text( 'Switch 1', style: TextStyle(fontSize: 16, color: Colors.white), ), SizedBox(height: 8), Transform.scale( scale: 1.4, child: StreamBuilder( stream: _channelRef.child('channel1').onValue, builder: (context, snapshot) { if (!snapshot.hasData || snapshot.hasError) { return CircularProgressIndicator(); // Placeholder jika data belum dimuat } var status = snapshot.data!.snapshot.value ?? false; return Switch( value: status == "On", activeColor: Theme.of(context).primaryColor, onChanged: (newValue) { setState(() { _updateChannelStatus('channel1', newValue); }); }, ); }, ), ), ], ), Column( children: [ Text( 'Switch 2', style: TextStyle(fontSize: 16, color: Colors.white), ), SizedBox(height: 8), Transform.scale( scale: 1.4, child: StreamBuilder( stream: _channelRef.child('channel2').onValue, builder: (context, snapshot) { if (!snapshot.hasData || snapshot.hasError) { return CircularProgressIndicator(); // Placeholder jika data belum dimuat } var status = snapshot.data!.snapshot.value ?? false; return Switch( value: status == "On", activeColor: Theme.of(context).primaryColor, onChanged: (newValue) { setState(() { _updateChannelStatus('channel2', newValue); }); }, ); }, ), ), ], ), ], ), SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Column( children: [ Text( 'Switch 3', style: TextStyle(fontSize: 16, color: Colors.white), ), SizedBox(height: 8), Transform.scale( scale: 1.4, child: StreamBuilder( stream: _channelRef.child('channel3').onValue, builder: (context, snapshot) { if (!snapshot.hasData || snapshot.hasError) { return CircularProgressIndicator(); // Placeholder jika data belum dimuat } var status = snapshot.data!.snapshot.value ?? false; return Switch( value: status == "On", activeColor: Theme.of(context).primaryColor, onChanged: (newValue) { setState(() { _updateChannelStatus('channel3', newValue); }); }, ); }, ), ), ], ), Column( children: [ Text( 'Switch 4', style: TextStyle(fontSize: 16, color: Colors.white), ), SizedBox(height: 8), Transform.scale( scale: 1.4, child: StreamBuilder( stream: _channelRef.child('channel4').onValue, builder: (context, snapshot) { if (!snapshot.hasData || snapshot.hasError) { return CircularProgressIndicator(); // Placeholder jika data belum dimuat } var status = snapshot.data!.snapshot.value ?? false; return Switch( value: status == "On", activeColor: Theme.of(context).primaryColor, onChanged: (newValue) { setState(() { _updateChannelStatus('channel4', newValue); }); }, ); }, ), ), ], ), ], ), ], ), ); } } class _TokenInputFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue) { if (newValue.selection.baseOffset == 0) { return newValue; } final StringBuffer newText = StringBuffer(); for (int i = 0; i < newValue.text.length; i++) { if (i > 0 && i % 4 == 0) { newText.write(' '); } newText.write(newValue.text[i]); } return TextEditingValue( text: newText.toString(), selection: TextSelection.collapsed(offset: newText.length), ); } }