507 lines
11 KiB
Dart
507 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:firebase_database/firebase_database.dart';
|
|
import 'setpoint_screen.dart';
|
|
|
|
class HomeScreen extends StatefulWidget {
|
|
const HomeScreen({super.key});
|
|
|
|
@override
|
|
State<HomeScreen> createState() => _HomeScreenState();
|
|
}
|
|
|
|
class _HomeScreenState extends State<HomeScreen> {
|
|
final DatabaseReference dbRef =
|
|
FirebaseDatabase.instance.ref("hydroponic/monitoring");
|
|
|
|
final DatabaseReference controlRef =
|
|
FirebaseDatabase.instance.ref("hydroponic/control");
|
|
|
|
|
|
double ph = 0.0;
|
|
double ppm = 0.0;
|
|
|
|
int waterAir = 0;
|
|
int waterAbmix = 0;
|
|
int waterPHUp = 0;
|
|
int waterPHDown = 0;
|
|
|
|
bool relayAir = false;
|
|
bool relayNutrisi = false;
|
|
bool relayPhUp = false;
|
|
bool relayPhDown = false;
|
|
|
|
String statusPh = "-";
|
|
String statusTds = "-";
|
|
String wifiStatus = "-";
|
|
|
|
String mode = "AUTO";
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
print("Firebase path: ${dbRef.path}");
|
|
|
|
dbRef.onValue.listen(
|
|
(event) {
|
|
print("===== FIREBASE EVENT =====");
|
|
print(event.snapshot.value);
|
|
|
|
if (event.snapshot.value == null) {
|
|
print("DATA NULL");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
final data =
|
|
Map<dynamic, dynamic>.from(event.snapshot.value as Map);
|
|
|
|
setState(() {
|
|
ph = double.tryParse(data['ph'].toString()) ?? 0.0;
|
|
ppm = double.tryParse(data['ppm'].toString()) ?? 0.0;
|
|
|
|
waterAir = data['water_air'] ?? 0;
|
|
waterAbmix = data['water_abmix'] ?? 0;
|
|
waterPHUp = data['water_ph_up'] ?? 0;
|
|
waterPHDown = data['water_ph_down'] ?? 0;
|
|
|
|
relayAir = data['relay_air'] ?? false;
|
|
relayNutrisi = data['relay_nutrisi'] ?? false;
|
|
relayPhUp = data['relay_ph_up'] ?? false;
|
|
relayPhDown = data['relay_ph_down'] ?? false;
|
|
|
|
statusPh = data['status_ph']?.toString() ?? "-";
|
|
statusTds = data['status_tds']?.toString() ?? "-";
|
|
wifiStatus = data['wifi_status']?.toString() ?? "-";
|
|
});
|
|
|
|
print("PH = $ph");
|
|
print("PPM = $ppm");
|
|
print("STATUS PH = $statusPh");
|
|
print("STATUS TDS = $statusTds");
|
|
} catch (e) {
|
|
print("ERROR FIREBASE: $e");
|
|
}
|
|
},
|
|
onError: (error) {
|
|
print("FIREBASE ERROR:");
|
|
print(error);
|
|
},
|
|
);
|
|
controlRef.onValue.listen((event) {
|
|
if (event.snapshot.value == null) return;
|
|
|
|
try {
|
|
final data =
|
|
Map<dynamic, dynamic>.from(event.snapshot.value as Map);
|
|
|
|
setState(() {
|
|
mode = data['mode']?.toString() ?? "AUTO";
|
|
|
|
relayAir = data['relay_air'] ?? false;
|
|
relayNutrisi = data['relay_nutrisi'] ?? false;
|
|
relayPhUp = data['relay_ph_up'] ?? false;
|
|
relayPhDown = data['relay_ph_down'] ?? false;
|
|
});
|
|
print("MODE = $mode");
|
|
} catch (e) {
|
|
print("CONTROL ERROR: $e");
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget sensorCard(
|
|
String title,
|
|
String value,
|
|
String status,
|
|
Color color,
|
|
) {
|
|
return Card(
|
|
elevation: 5,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Row(
|
|
children: [
|
|
CircleAvatar(
|
|
radius: 35,
|
|
backgroundColor: color,
|
|
child: Text(
|
|
value,
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 15),
|
|
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 18,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 5),
|
|
|
|
Text(
|
|
status,
|
|
style: TextStyle(
|
|
color: color,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget statusRow(
|
|
IconData icon,
|
|
String title,
|
|
bool status,
|
|
) {
|
|
return Card(
|
|
elevation: 3,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(15),
|
|
),
|
|
child: ListTile(
|
|
leading: Icon(
|
|
icon,
|
|
color: status ? Colors.green : Colors.grey,
|
|
),
|
|
title: Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
trailing: Chip(
|
|
backgroundColor:
|
|
status ? Colors.green : Colors.red,
|
|
label: Text(
|
|
status ? "ON" : "OFF",
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
Widget manualButton(
|
|
String title,
|
|
String firebaseKey,
|
|
bool currentValue,
|
|
) {
|
|
return Card(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(15),
|
|
),
|
|
child: SwitchListTile(
|
|
title: Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
value: currentValue,
|
|
activeColor: Colors.green,
|
|
onChanged: (value) async {
|
|
await controlRef
|
|
.child(firebaseKey)
|
|
.set(value);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
String getWaterLevelWarning() {
|
|
List<String> warningList = [];
|
|
|
|
if (waterAir < 1000) {
|
|
warningList.add("Tangki Air Bersih");
|
|
}
|
|
|
|
if (waterAbmix < 1000) {
|
|
warningList.add("Tangki AB Mix");
|
|
}
|
|
|
|
if (waterPHUp < 1000) {
|
|
warningList.add("Tangki pH Up");
|
|
}
|
|
|
|
if (waterPHDown < 1000) {
|
|
warningList.add("Tangki pH Down");
|
|
}
|
|
|
|
return warningList.join(", ");
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final phColor =
|
|
(ph >= 5.5 && ph <= 6.5) ? Colors.green : Colors.red;
|
|
|
|
final ppmColor =
|
|
(ppm >= 800 && ppm <= 1100) ? Colors.green : Colors.orange;
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.eco),
|
|
SizedBox(width: 8),
|
|
Text("Eco Green House"),
|
|
],
|
|
),
|
|
centerTitle: true,
|
|
backgroundColor: Colors.green,
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.settings),
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const SetpointScreen(),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
|
|
body: ListView(
|
|
padding: const EdgeInsets.all(12),
|
|
children: [
|
|
|
|
if (
|
|
waterAir < 1000 ||
|
|
waterAbmix < 1000 ||
|
|
waterPHUp < 1000 ||
|
|
waterPHDown < 1000
|
|
)
|
|
Container(
|
|
margin: const EdgeInsets.only(bottom: 10),
|
|
padding: const EdgeInsets.all(15),
|
|
decoration: BoxDecoration(
|
|
color: Colors.red,
|
|
borderRadius: BorderRadius.circular(15),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
const Icon(
|
|
Icons.warning,
|
|
color: Colors.white,
|
|
),
|
|
const SizedBox(width: 10),
|
|
Expanded(
|
|
child: Text(
|
|
"${getWaterLevelWarning()} rendah, segera isi tangki!",
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: Colors.green,
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
const Icon(
|
|
Icons.eco,
|
|
color: Colors.white,
|
|
size: 60,
|
|
),
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
const Text(
|
|
"Eco Green House",
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 5),
|
|
|
|
Text(
|
|
"WiFi : $wifiStatus",
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 15),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
children: [
|
|
const Icon(
|
|
Icons.science,
|
|
color: Colors.blue,
|
|
size: 35,
|
|
),
|
|
const SizedBox(height: 8),
|
|
const Text(
|
|
"pH",
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
Text(
|
|
ph.toStringAsFixed(2),
|
|
style: const TextStyle(
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
Expanded(
|
|
child: Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
children: [
|
|
const Icon(
|
|
Icons.water_drop,
|
|
color: Colors.green,
|
|
size: 35,
|
|
),
|
|
const SizedBox(height: 8),
|
|
const Text(
|
|
"PPM",
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
Text(
|
|
ppm.toStringAsFixed(0),
|
|
style: const TextStyle(
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
Card(
|
|
child: ListTile(
|
|
title: const Text("Mode Sistem"),
|
|
subtitle: Text("Status: $mode"),
|
|
trailing: ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor:
|
|
mode == "AUTO" ? Colors.green : Colors.orange,
|
|
),
|
|
onPressed: () {
|
|
final newMode = mode == "AUTO" ? "MANUAL" : "AUTO";
|
|
controlRef.child("mode").set(newMode);
|
|
},
|
|
child: Text(
|
|
mode == "AUTO" ? "Switch MANUAL" : "Switch AUTO",
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
if (mode == "MANUAL") ...[
|
|
manualButton("Pompa Air", "relay_air", relayAir),
|
|
manualButton("Pompa Nutrisi", "relay_nutrisi", relayNutrisi),
|
|
manualButton("Pompa pH Up", "relay_ph_up", relayPhUp),
|
|
manualButton("Pompa pH Down", "relay_ph_down", relayPhDown),
|
|
const SizedBox(height: 10),
|
|
],
|
|
|
|
statusRow(
|
|
Icons.arrow_upward,
|
|
"Pompa pH Up",
|
|
relayPhUp,
|
|
),
|
|
|
|
statusRow(
|
|
Icons.arrow_downward,
|
|
"Pompa pH Down",
|
|
relayPhDown,
|
|
),
|
|
|
|
statusRow(
|
|
Icons.eco,
|
|
"Pompa Nutrisi",
|
|
relayNutrisi,
|
|
),
|
|
|
|
statusRow(
|
|
Icons.water_drop,
|
|
"Pompa Air",
|
|
relayAir,
|
|
),
|
|
|
|
Card(
|
|
child: ListTile(
|
|
leading: const Icon(Icons.wifi),
|
|
title: const Text("WiFi Status"),
|
|
trailing: Text(
|
|
wifiStatus,
|
|
style: TextStyle(
|
|
color: wifiStatus == "connected"
|
|
? Colors.green
|
|
: Colors.red,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
],
|
|
),
|
|
);
|
|
}
|
|
} |