USER
"Buat aplikasi menggunakan React Native Expo yang bisa berjalan di Android dan iOS untuk mengendalikan Automatic Fish Feeder dengan fitur berikut:
Fitur Kontrol IoT:
Aplikasi harus bisa terkoneksi ke satu atau beberapa perangkat IoT (Automatic Fish Feeder) melalui HiveMQ Cloud MQTT Broker menggunakan protokol MQTT.
Setiap perangkat IoT (ESP8266 + servo) harus bisa diatur jadwal gerakan servonya. Contoh: pengguna ingin servo bergerak pada jam 10.00 pagi, 13.00 siang, dan 20.00 malam, dengan setiap kali pemberian pakan servo harus bergerak 3 kali.
Aplikasi harus memungkinkan pengguna untuk membuat, membaca, mengupdate, dan menghapus jadwal ini (CRUD operations).
Fitur Library Aquarium Biota:
Tampilkan informasi biota aquarium dengan data berikut: nama biota, tipe biota, makanan biota, biota yang kompatibel, ukuran minimum tangki, tipe air, substrat, deskripsi, dan URL gambar biota.
Data biota ini disimpan dalam MongoDB, dan bisa diakses pengguna dari aplikasi melalui API yang dibuat dengan Koa.js.
Fitur Manage Aquarium dengan Algoritma Kompatibilitas Biota:
Buat fitur Manage Aquarium seperti yang terlihat pada diagram yang saya lampirkan, di mana pengguna dapat memilih beberapa biota untuk akuariumnya.
Ketika pengguna memilih biota jenis A, buat algoritma yang secara otomatis memfilter dan tidak menampilkan biota yang tidak kompatibel (contoh: jika biota A tidak cocok dengan biota B dan C berdasarkan data compatible biota yang ada di database, maka biota B dan C tidak akan muncul sebagai pilihan).
Algoritma kompatibilitas ini harus diambil dari field compatible biota yang tersimpan dalam database MongoDB.
Fitur Autentikasi:
Implementasikan autentikasi pengguna menggunakan Bearer Token. Pengguna bisa login dan register melalui aplikasi.
Backend harus menggunakan Koa.js dan mendukung autentikasi JWT (JSON Web Token).
Backend & Database:
Backend harus menggunakan Koa.js untuk mengatur API.
Simpan data pengguna, jadwal kontrol IoT, dan data biota di MongoDB.
Gunakan MQTT untuk komunikasi antara aplikasi dan perangkat IoT melalui HiveMQ Cloud MQTT Broker.
Koneksi WiFi ESP8266:
Untuk menghubungkan perangkat ke WiFi, gunakan WiFi Manager pada ESP8266 sehingga pengguna dapat mengatur SSID dan password WiFi langsung dari aplikasi.
Tambahkan Notifikasi:
Implementasikan notifikasi untuk mengingatkan pengguna ketika jadwal pemberian pakan mendekati waktu yang ditetapkan.
Struktur aplikasi:
Buat halaman login/register, halaman daftar perangkat IoT, halaman pengaturan jadwal untuk setiap perangkat, halaman Manage Aquarium yang memungkinkan pengguna untuk memilih biota, dan halaman Library Biota.
Pada fitur Manage Aquarium, pastikan algoritma filter kompatibilitas biota bekerja untuk memastikan hanya biota yang cocok yang bisa dipilih.
Untuk setiap fitur yang dijelaskan, tambahkan penjelasan kode yang detail sehingga mudah dipahami.
Framework yang digunakan:
Frontend: React Native Expo untuk Android dan iOS.
Backend: Koa.js sebagai server backend.
Database: MongoDB untuk penyimpanan data.
MQTT untuk komunikasi IoT menggunakan HiveMQ Cloud MQTT Broker.
ESP8266 dihubungkan ke WiFi menggunakan WiFi Manager.
Pastikan untuk memberikan kode lengkap, termasuk React Native, Backend Koa, MongoDB schema, algoritma kompatibilitas biota, dan cara koneksi MQTT dengan ESP8266."
ASSISTANT
Berikut adalah panduan lengkap untuk membuat aplikasi pengendali **Automatic Fish Feeder** menggunakan **React Native Expo** untuk Android dan iOS, dengan backend menggunakan **Koa.js**, database **MongoDB**, dan komunikasi **MQTT** melalui **HiveMQ Cloud MQTT Broker**. Selain itu, pengaturan WiFi pada **ESP8266** akan menggunakan **WiFi Manager**.
---
## Daftar Isi
1. [Frontend dengan React Native Expo](#1-frontend-dengan-react-native-expo)
- a. Halaman Login & Registrasi
- b. Halaman Daftar Perangkat IoT
- c. Halaman Pengaturan Jadwal
- d. Halaman Library Biota
- e. Halaman Manage Aquarium
2. [Backend dengan Koa.js](#2-backend-dengan-koajs)
- a. Setup Server Koa.js
- b. Autentikasi JWT
- c. API untuk Perangkat & Jadwal
- d. API untuk Biota
3. [Database dengan MongoDB](#3-database-dengan-mongodb)
- a. Skema Pengguna
- b. Skema Perangkat & Jadwal
- c. Skema Biota
4. [Algoritma Kompatibilitas Biota](#4-algoritma-kompatibilitas-biota)
5. [ESP8266 dengan WiFi Manager & MQTT](#5-esp8266-dengan-wifi-manager--mqtt)
- a. Pengaturan WiFi Manager
- b. Koneksi MQTT pada ESP8266
6. [Implementasi Notifikasi](#6-implementasi-notifikasi)
7. [Penjelasan Kode Detil](#7-penjelasan-kode-detil)
---
## 1. Frontend dengan React Native Expo
### a. Halaman Login & Registrasi
**File:** `LoginScreen.js` dan `RegisterScreen.js`
**LoginScreen.js**
```javascript
import React, { useState } from 'react';
import { View, TextInput, Button, Text } from 'react-native';
const LoginScreen = ({ navigation }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleLogin = () => {
// Panggil API login backend
fetch('http://your-backend-url.com/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
})
.then(response => response.json())
.then(data => {
if (data.token) {
// Simpan token dan navigasi ke halaman berikutnya
}
})
.catch(error => console.error(error));
};
return (
<View>
<Text>Email:</Text>
<TextInput
value={email}
onChangeText={setEmail}
placeholder="Masukkan email"
/>
<Text>Password:</Text>
<TextInput
value={password}
onChangeText={setPassword}
placeholder="Masukkan password"
secureTextEntry
/>
<Button title="Login" onPress={handleLogin} />
<Button
title="Belum punya akun? Daftar"
onPress={() => navigation.navigate('Register')}
/>
</View>
);
};
export default LoginScreen;
```
**Penjelasan:**
- Menggunakan `useState` untuk mengelola state `email` dan `password`.
- `handleLogin` akan memanggil API login yang kita buat di backend.
- Jika login berhasil, token JWT akan disimpan (misalnya di AsyncStorage) untuk autentikasi selanjutnya.
### b. Halaman Daftar Perangkat IoT
**File:** `DeviceListScreen.js`
```javascript
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, Button } from 'react-native';
const DeviceListScreen = ({ navigation }) => {
const [devices, setDevices] = useState([]);
useEffect(() => {
// Ambil daftar perangkat dari backend
fetch('http://your-backend-url.com/api/devices', {
headers: {
Authorization: 'Bearer YOUR_TOKEN',
},
})
.then(response => response.json())
.then(data => setDevices(data.devices))
.catch(error => console.error(error));
}, []);
return (
<View>
<FlatList
data={devices}
keyExtractor={item => item._id}
renderItem={({ item }) => (
<View>
<Text>{item.name}</Text>
<Button
title="Atur Jadwal"
onPress={() => navigation.navigate('Schedule', { deviceId: item._id })}
/>
</View>
)}
/>
</View>
);
};
export default DeviceListScreen;
```
**Penjelasan:**
- Menggunakan `useEffect` untuk mengambil data perangkat saat komponen dimuat.
- Menampilkan daftar perangkat dalam `FlatList`.
- Setiap perangkat memiliki tombol untuk mengatur jadwal, yang akan membawa pengguna ke `ScheduleScreen` dengan `deviceId` sebagai parameter.
### c. Halaman Pengaturan Jadwal
**File:** `ScheduleScreen.js`
```javascript
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, Button, TextInput } from 'react-native';
const ScheduleScreen = ({ route }) => {
const { deviceId } = route.params;
const [schedules, setSchedules] = useState([]);
const [time, setTime] = useState('');
const [feedCount, setFeedCount] = useState(1);
useEffect(() => {
// Ambil jadwal dari backend
fetch(`http://your-backend-url.com/api/devices/${deviceId}/schedules`, {
headers: {
Authorization: 'Bearer YOUR_TOKEN',
},
})
.then(response => response.json())
.then(data => setSchedules(data.schedules))
.catch(error => console.error(error));
}, []);
const addSchedule = () => {
// Tambahkan jadwal baru
fetch(`http://your-backend-url.com/api/devices/${deviceId}/schedules`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer YOUR_TOKEN',
},
body: JSON.stringify({ time, feedCount }),
})
.then(response => response.json())
.then(newSchedule => {
setSchedules([...schedules, newSchedule]);
setTime('');
setFeedCount(1);
})
.catch(error => console.error(error));
};
return (
<View>
<TextInput
value={time}
onChangeText={setTime}
placeholder="Masukkan waktu (HH:MM)"
/>
<TextInput
value={feedCount.toString()}
onChangeText={text => setFeedCount(parseInt(text))}
placeholder="Jumlah pemberian pakan"
keyboardType="numeric"
/>
<Button title="Tambah Jadwal" onPress={addSchedule} />
<FlatList
data={schedules}
keyExtractor={item => item._id}
renderItem={({ item }) => (
<View>
<Text>{item.time} - {item.feedCount} kali</Text>
{/* Tambahkan tombol untuk update dan delete jika diperlukan */}
</View>
)}
/>
</View>
);
};
export default ScheduleScreen;
```
**Penjelasan:**
- Mengambil dan menampilkan jadwal yang sudah ada.
- Memungkinkan pengguna untuk menambahkan jadwal baru.
- Mengirim data ke backend untuk disimpan di database.
### d. Halaman Library Biota
**File:** `BiotaLibraryScreen.js`
```javascript
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, Image } from 'react-native';
const BiotaLibraryScreen = () => {
const [biotas, setBiotas] = useState([]);
useEffect(() => {
// Ambil data biota dari backend
fetch('http://your-backend-url.com/api/biotas', {
headers: {
Authorization: 'Bearer YOUR_TOKEN',
},
})
.then(response => response.json())
.then(data => setBiotas(data.biotas))
.catch(error => console.error(error));
}, []);
return (
<View>
<FlatList
data={biotas}
keyExtractor={item => item._id}
renderItem={({ item }) => (
<View>
<Text>{item.name}</Text>
<Image source={{ uri: item.imageUrl }} style={{ width: 100, height: 100 }} />
<Text>Tipe: {item.type}</Text>
<Text>Makanan: {item.food}</Text>
<Text>Deskripsi: {item.description}</Text>
{/* Tampilkan informasi lain sesuai kebutuhan */}
</View>
)}
/>
</View>
);
};
export default BiotaLibraryScreen;
```
**Penjelasan:**
- Menampilkan daftar biota lengkap dengan informasi dan gambar.
- Data diambil dari backend dan ditampilkan menggunakan `FlatList`.
### e. Halaman Manage Aquarium
**File:** `ManageAquariumScreen.js`
```javascript
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, Button } from 'react-native';
const ManageAquariumScreen = () => {
const [selectedBiotas, setSelectedBiotas] = useState([]);
const [availableBiotas, setAvailableBiotas] = useState([]);
useEffect(() => {
// Ambil data biota dari backend
fetch('http://your-backend-url.com/api/biotas', {
headers: {
Authorization: 'Bearer YOUR_TOKEN',
},
})
.then(response => response.json())
.then(data => setAvailableBiotas(data.biotas))
.catch(error => console.error(error));
}, []);
const addBiota = (biota) => {
// Tambahkan biota ke akuarium
setSelectedBiotas([...selectedBiotas, biota]);
// Filter biota yang tidak kompatibel
const compatibleBiotas = availableBiotas.filter(item =>
biota.compatibleBiotas.includes(item.name)
);
setAvailableBiotas(compatibleBiotas);
};
return (
<View>
<Text>Biota yang Dipilih:</Text>
<FlatList
data={selectedBiotas}
keyExtractor={item => item._id}
renderItem={({ item }) => (
<Text>{item.name}</Text>
)}
/>
<Text>Pilih Biota:</Text>
<FlatList
data={availableBiotas}
keyExtractor={item => item._id}
renderItem={({ item }) => (
<View>
<Text>{item.name}</Text>
<Button title="Tambah" onPress={() => addBiota(item)} />
</View>
)}
/>
</View>
);
};
export default ManageAquariumScreen;
```
**Penjelasan:**
- Menampilkan biota yang sudah dipilih oleh pengguna.
- Saat pengguna menambahkan biota, sistem akan memfilter `availableBiotas` berdasarkan kompatibilitas.
- Kompatibilitas diambil dari field `compatibleBiotas` pada setiap biota.
## 2. Backend dengan Koa.js
### a. Setup Server Koa.js
**File:** `app.js`
```javascript
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const mongoose = require('mongoose');
const router = require('./routes'); // Import routes
const app = new Koa();
mongoose.connect('mongodb://localhost:27017/fish_feeder', { useNewUrlParser: true, useUnifiedTopology: true });
app.use(bodyParser());
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => {
console.log('Server berjalan di port 3000');
});
```
**Penjelasan:**
- Menginisialisasi Koa.js dan menghubungkan ke MongoDB.
- Menggunakan `koa-bodyparser` untuk parsing request body.
- Menggunakan `router` untuk mendefinisikan endpoint API.
### b. Autentikasi JWT
**File:** `routes/auth.js`
```javascript
const Router = require('@koa/router');
const jwt = require('jsonwebtoken');
const User = require('../models/User'); // Model pengguna
const router = new Router();
router.post('/register', async (ctx) => {
const { email, password } = ctx.request.body;
const user = new User({ email, password });
await user.save();
ctx.body = { message: 'Registrasi berhasil' };
});
router.post('/login', async (ctx) => {
const { email, password } = ctx.request.body;
const user = await User.findOne({ email });
if (!user || user.password !== password) {
ctx.status = 401;
ctx.body = { message: 'Email atau password salah' };
return;
}
const token = jwt.sign({ id: user._id }, 'SECRET_KEY');
ctx.body = { token };
});
module.exports = router;
```
**Penjelasan:**
- Menyediakan endpoint untuk registrasi dan login.
- Saat login berhasil, server akan mengirimkan token JWT yang berisi ID pengguna.
- Token ini akan digunakan untuk autentikasi pada endpoint lain.
**Middleware untuk Autentikasi**
**File:** `middleware/auth.js`
```javascript
const jwt = require('jsonwebtoken');
const auth = async (ctx, next) => {
const token = ctx.headers.authorization && ctx.headers.authorization.split(' ')[1];
if (!token) {
ctx.status = 401;
ctx.body = { message: 'Token tidak ditemukan' };
return;
}
try {
const decoded = jwt.verify(token, 'SECRET_KEY');
ctx.state.user = decoded;
await next();
} catch (err) {
ctx.status = 401;
ctx.body = { message: 'Token tidak valid' };
}
};
module.exports = auth;
```
**Penjelasan:**
- Middleware untuk memvalidasi token JWT.
- Jika token valid, middleware akan melanjutkan ke handler berikutnya.
- Jika tidak, akan mengembalikan status 401 Unauthorized.
### c. API untuk Perangkat & Jadwal
**File:** `routes/devices.js`
```javascript
const Router = require('@koa/router');
const auth = require('../middleware/auth');
const Device = require('../models/Device');
const router = new Router();
router.use(auth); // Semua route di bawah ini memerlukan autentikasi
router.get('/devices', async (ctx) => {
const devices = await Device.find({ userId: ctx.state.user.id });
ctx.body = { devices };
});
router.post('/devices', async (ctx) => {
const { name } = ctx.request.body;
const device = new Device({ name, userId: ctx.state.user.id });
await device.save();
ctx.body = { device };
});
router.get('/devices/:id/schedules', async (ctx) => {
const device = await Device.findById(ctx.params.id);
if (device.userId != ctx.state.user.id) {
ctx.status = 403;
return;
}
ctx.body = { schedules: device.schedules };
});
router.post('/devices/:id/schedules', async (ctx) => {
const device = await Device.findById(ctx.params.id);
if (device.userId != ctx.state.user.id) {
ctx.status = 403;
return;
}
const { time, feedCount } = ctx.request.body;
device.schedules.push({ time, feedCount });
await device.save();
ctx.body = { schedule: device.schedules[device.schedules.length - 1] };
});
module.exports = router;
```
**Penjelasan:**
- Endpoint untuk mengambil dan menambahkan perangkat dan jadwal.
- Menggunakan middleware `auth` untuk memastikan pengguna terautentikasi.
- Melakukan pemeriksaan kepemilikan perangkat sebelum melakukan operasi.
### d. API untuk Biota
**File:** `routes/biotas.js`
```javascript
const Router = require('@koa/router');
const Biota = require('../models/Biota');
const auth = require('../middleware/auth');
const router = new Router();
router.use(auth); // Semua route di bawah ini memerlukan autentikasi
router.get('/biotas', async (ctx) => {
const biotas = await Biota.find();
ctx.body = { biotas };
});
// Tambahkan endpoint lain sesuai kebutuhan (misalnya untuk menambah atau mengupdate biota)
module.exports = router;
```
**Penjelasan:**
- Endpoint untuk mengambil data biota dari database.
- Menggunakan autentikasi untuk memastikan hanya pengguna terdaftar yang dapat mengakses data.
## 3. Database dengan MongoDB
### a. Skema Pengguna
**File:** `models/User.js`
```javascript
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
email: String,
password: String,
});
module.exports = mongoose.model('User', userSchema);
```
**Penjelasan:**
- Skema sederhana untuk menyimpan email dan password pengguna.
- Dalam produksi, sebaiknya password dienkripsi menggunakan bcrypt.
### b. Skema Perangkat & Jadwal
**File:** `models/Device.js`
```javascript
const mongoose = require('mongoose');
const scheduleSchema = new mongoose.Schema({
time: String, // Format HH:MM
feedCount: Number,
});
const deviceSchema = new mongoose.Schema({
name: String,
userId: String,
schedules: [scheduleSchema],
});
module.exports = mongoose.model('Device', deviceSchema);
```
**Penjelasan:**
- Setiap perangkat memiliki nama, `userId` pemilik, dan array jadwal.
- Jadwal terdiri dari waktu dan jumlah pemberian pakan.
### c. Skema Biota
**File:** `models/Biota.js`
```javascript
const mongoose = require('mongoose');
const biotaSchema = new mongoose.Schema({
name: String,
type: String,
food: String,
compatibleBiotas: [String], // Nama biota yang kompatibel
minTankSize: Number,
waterType: String,
substrate: String,
description: String,
imageUrl: String,
});
module.exports = mongoose.model('Biota', biotaSchema);
```
**Penjelasan:**
- Menyimpan informasi lengkap tentang biota.
- Field `compatibleBiotas` berisi array nama biota yang kompatibel.
## 4. Algoritma Kompatibilitas Biota
Pada frontend, saat pengguna menambahkan biota ke akuarium, kita perlu memfilter pilihan biota berikutnya berdasarkan kompatibilitas.
**Implementasi di `ManageAquariumScreen.js` sudah mencakup algoritma ini:**
```javascript
const addBiota = (biota) => {
// Tambahkan biota ke akuarium
setSelectedBiotas([...selectedBiotas, biota]);
// Filter biota yang kompatibel dengan semua biota yang sudah dipilih
const compatibleBiotas = availableBiotas.filter(item => {
return selectedBiotas.every(selectedBiota => {
return selectedBiota.compatibleBiotas.includes(item.name);
});
});
setAvailableBiotas(compatibleBiotas);
};
```
**Penjelasan:**
- Saat biota ditambahkan, kita memperbarui `selectedBiotas`.
- `availableBiotas` diperbarui dengan memfilter biota yang kompatibel dengan semua biota yang sudah dipilih.
- Ini memastikan bahwa hanya biota yang kompatibel yang dapat dipilih selanjutnya.
## 5. ESP8266 dengan WiFi Manager & MQTT
### a. Pengaturan WiFi Manager
Menggunakan **WiFiManager** library untuk ESP8266.
**Kode ESP8266:**
```cpp
#include <ESP8266WiFi.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
void setup() {
Serial.begin(115200);
// Inisialisasi WiFi Manager
WiFiManager wifiManager;
// wifiManager.resetSettings(); // Hapus konfigurasi sebelumnya jika perlu
wifiManager.autoConnect("AutoConnectAP");
Serial.println("Connected.");
}
void loop() {
// Kode utama
}
```
**Penjelasan:**
- WiFiManager akan membuat Access Point bernama "AutoConnectAP" jika tidak ada jaringan tersimpan.
- Pengguna dapat menghubungkan ponsel ke AP ini dan mengakses halaman konfigurasi untuk memasukkan SSID dan password WiFi.
- Setelah terhubung, perangkat akan mengingat SSID dan password tersebut.
### b. Koneksi MQTT pada ESP8266
Menggunakan **PubSubClient** library untuk MQTT.
**Kode ESP8266 Lanjutan:**
```cpp
#include <ESP8266WiFi.h>
#include <WiFiManager.h>
#include <PubSubClient.h>
const char* mqtt_server = "your-hivemq-cloud-mqtt-broker";
WiFiClient espClient;
PubSubClient client(espClient);
void callback(char* topic, byte* payload, unsigned int length) {
// Handle pesan MQTT di sini
Serial.print("Message arrived [");
Serial.print(topic);
Serial.println("] ");
// Contoh: Menggerakkan servo sesuai perintah
}
void setup() {
Serial.begin(115200);
WiFiManager wifiManager;
wifiManager.autoConnect("AutoConnectAP");
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
Serial.println("Connected to WiFi, connecting to MQTT broker...");
reconnect();
}
void reconnect() {
// Loop sampai terhubung
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Client ID
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
// Coba terhubung
if (client.connect(clientId.c_str())) {
Serial.println("connected");
// Subscribe ke topic tertentu
client.subscribe("fishfeeder/device1");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Tunggu 5 detik sebelum mencoba lagi
delay(5000);
}
}
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
}
```
**Penjelasan:**
- Mengatur koneksi ke **MQTT broker** menggunakan `PubSubClient`.
- Fungsi `callback` akan dipanggil saat pesan diterima.
- Pada `setup`, perangkat mencoba terhubung ke MQTT broker dan subscribe ke topic spesifik.
- Pada `loop`, perangkat memastikan tetap terhubung dan memanggil `client.loop()`.
## 6. Implementasi Notifikasi
Pada aplikasi React Native, kita dapat menggunakan **Expo Notifications** untuk mengirim notifikasi lokal.
**Menginstal Library Notifications:**
```bash
expo install expo-notifications
```
**Konfigurasi Notifikasi:**
**File:** `NotificationService.js`
```javascript
import * as Notifications from 'expo-notifications';
import * as Permissions from 'expo-permissions';
export const scheduleNotification = async (time) => {
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
if (status !== 'granted') {
alert('Izin notifikasi tidak diberikan');
return;
}
const trigger = new Date(time);
trigger.setMinutes(trigger.getMinutes() - 10); // 10 menit sebelum waktu sebenarnya
await Notifications.scheduleNotificationAsync({
content: {
title: "Waktunya memberi makan ikan!",
body: `Pada pukul ${time}`,
},
trigger,
});
};
```
**Memanggil `scheduleNotification` saat menambahkan jadwal:**
```javascript
// Dalam ScheduleScreen.js, setelah menambahkan jadwal
addSchedule = () => {
// ... kode sebelumnya
scheduleNotification(time);
};
```
**Penjelasan:**
- Menggunakan `Notifications` dari Expo untuk menjadwalkan notifikasi lokal.
- Notifikasi dijadwalkan untuk muncul beberapa menit sebelum waktu yang ditentukan.
- Pastikan untuk meminta izin notifikasi dari pengguna.
## 7. Penjelasan Kode Detil
**Untuk setiap bagian kode di atas, berikut adalah penjelasan tambahan:**
- **State Management:** Menggunakan `useState` dan `useEffect` dari React Hooks untuk mengelola state dan efek samping seperti pengambilan data.
- **Navigation:** Menggunakan React Navigation (tidak ditampilkan kode impor dan setup) untuk berpindah antar layar.
- **API Calls:** Menggunakan `fetch` API untuk berkomunikasi dengan backend. Pastikan untuk menangani kasus error dan menampilkan pesan yang sesuai kepada pengguna.
- **Autentikasi:** Menyertakan header `Authorization` dengan token Bearer pada setiap permintaan yang memerlukan autentikasi.
- **Data Flow:** Data dari backend diteruskan ke komponen dan disimpan dalam state untuk ditampilkan atau digunakan dalam logika aplikasi.
- **Styling:** Untuk kejelasan, kode di atas mengabaikan styling. Dalam aplikasi nyata, gunakan `StyleSheet` untuk mendefinisikan gaya dan menerapkannya pada komponen.
- **Error Handling:** Penting untuk menangani kemungkinan error, misalnya gagal terhubung ke server, token kadaluarsa, dll.
- **Security:** Jangan pernah menyimpan token atau informasi sensitif dalam kode sumber. Gunakan mekanisme penyimpanan aman seperti `SecureStore` di Expo.
- **Cleanup:** Pastikan untuk membersihkan timer atau listener dalam `useEffect` jika diperlukan untuk mencegah memory leaks.
- **Responsive Design:** Perhatikan bahwa aplikasi harus responsif dan tampil baik di berbagai ukuran layar.
---
## Catatan Tambahan
- **Pengembangan dan Pengujian:** Pastikan untuk melakukan pengujian pada setiap bagian aplikasi, baik frontend maupun backend, untuk memastikan semua fitur berfungsi dengan baik.
- **Deployment:** Untuk backend, pertimbangkan untuk menggunakan layanan seperti Heroku atau VPS untuk hosting server Koa.js dan MongoDB.
- **Keamanan:** Implementasikan praktik keamanan terbaik, seperti hashing password dengan bcrypt, validasi input, dan penanganan error yang aman.
- **Dokumentasi API:** Membuat dokumentasi API (misalnya dengan Swagger) akan membantu dalam pengembangan dan pemeliharaan aplikasi.
---
Dengan mengikuti panduan dan kode di atas, Anda dapat membangun aplikasi Automatic Fish Feeder dengan fitur yang diinginkan. Pastikan untuk menyesuaikan dan mengembangkan kode sesuai dengan kebutuhan spesifik proyek Anda.