560 lines
16 KiB
Markdown
560 lines
16 KiB
Markdown
# 🚪 Smart Rack Door Access (Reed Switch) API
|
|
|
|
Backend API untuk menerima data sensor Reed Switch yang mendeteksi buka tutup pintu dan mengirim notifikasi otomatis jika terdeteksi akses mencurigakan atau tidak sah.
|
|
|
|
## 📡 **API Endpoints**
|
|
|
|
### 1. **Kirim Data Sensor Reed Switch**
|
|
```http
|
|
POST /api/door-access/data
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"device_id": 1,
|
|
"door_opened": true,
|
|
"access_method": "keycard",
|
|
"user_id_card": "EMP-1234",
|
|
"duration_seconds": 45,
|
|
"door_location": "front_door",
|
|
"is_forced_entry": false,
|
|
"door_opened_at": "2026-04-28T10:30:00Z",
|
|
"door_closed_at": "2026-04-28T10:30:45Z",
|
|
"metadata": {
|
|
"sensor_type": "Reed_Switch_Magnetic",
|
|
"firmware_version": "3.1.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response Authorized (Jam Kerja + ID Card):**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "Door access data received successfully",
|
|
"data": {
|
|
"id": 127,
|
|
"door_opened": true,
|
|
"access_type": "authorized",
|
|
"is_authorized": true,
|
|
"is_suspicious": false,
|
|
"alert_sent": false,
|
|
"priority": "info",
|
|
"recorded_at": "2026-04-28T10:30:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response Unauthorized (Alert Triggered):**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "Door access data received successfully",
|
|
"data": {
|
|
"id": 128,
|
|
"door_opened": true,
|
|
"access_type": "forced_entry",
|
|
"is_authorized": false,
|
|
"is_suspicious": true,
|
|
"alert_sent": true,
|
|
"priority": "critical",
|
|
"recorded_at": "2026-04-28T22:15:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. **Ambil Data Door Access Terbaru**
|
|
```http
|
|
GET /api/door-access/readings?device_id=1&limit=50&access_type=unauthorized&door_location=front_door&suspicious=true
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"id": 128,
|
|
"device_id": 1,
|
|
"door_opened": true,
|
|
"is_authorized_access": false,
|
|
"access_type": "forced_entry",
|
|
"access_method": "force",
|
|
"user_id_card": null,
|
|
"duration_seconds": 180,
|
|
"is_suspicious": true,
|
|
"door_location": "front_door",
|
|
"is_forced_entry": true,
|
|
"door_opened_at": "2026-04-28T22:15:00Z",
|
|
"door_closed_at": "2026-04-28T22:18:00Z",
|
|
"recorded_at": "2026-04-28T22:15:00Z",
|
|
"device": {
|
|
"id": 1,
|
|
"name": "Reed Switch Pintu Utama",
|
|
"location": "Ruang Server"
|
|
}
|
|
}
|
|
],
|
|
"count": 1
|
|
}
|
|
```
|
|
|
|
### 3. **Ambil Statistik Door Access**
|
|
```http
|
|
GET /api/door-access/statistics?device_id=1&hours=24
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"total_access_attempts": 85,
|
|
"door_opened_count": 78,
|
|
"authorized_count": 65,
|
|
"unauthorized_count": 13,
|
|
"suspicious_count": 8,
|
|
"forced_entry_count": 2,
|
|
"authorized_percentage": 76.47,
|
|
"suspicious_percentage": 9.41,
|
|
"avg_duration": 42.5,
|
|
"max_duration": 180,
|
|
"latest_access": {
|
|
"id": 128,
|
|
"access_type": "forced_entry",
|
|
"user_id_card": null,
|
|
"recorded_at": "2026-04-28T22:15:00Z"
|
|
},
|
|
"access_types": {
|
|
"authorized": 65,
|
|
"unauthorized": 8,
|
|
"after_hours": 5,
|
|
"forced_entry": 2,
|
|
"emergency": 3,
|
|
"maintenance": 2
|
|
},
|
|
"door_locations": {
|
|
"front_door": 45,
|
|
"back_door": 20,
|
|
"side_door": 15,
|
|
"main_entrance": 5
|
|
},
|
|
"access_methods": {
|
|
"keycard": 60,
|
|
"manual": 15,
|
|
"force": 2,
|
|
"emergency": 3,
|
|
"maintenance": 2,
|
|
"unknown": 3
|
|
}
|
|
},
|
|
"period_hours": 24
|
|
}
|
|
```
|
|
|
|
### 4. **Test Notifikasi Door Access**
|
|
```http
|
|
POST /api/test-door-access-notification
|
|
```
|
|
|
|
## 🚨 **Sistem Deteksi & Alert**
|
|
|
|
### **Tipe Akses:**
|
|
- **authorized**: Akses sah dengan ID card dalam jam kerja
|
|
- **unauthorized**: Akses tanpa ID card atau ID tidak valid
|
|
- **after_hours**: Akses di luar jam kerja (07:00-18:00)
|
|
- **forced_entry**: Paksa masuk (CRITICAL ALERT)
|
|
- **emergency**: Akses darurat (diizinkan kapan saja)
|
|
- **maintenance**: Akses maintenance dalam jam kerja
|
|
|
|
### **Kondisi Mencurigakan:**
|
|
1. **Forced Entry** - Paksa masuk tanpa ID card
|
|
2. **Akses luar jam kerja** tanpa ID card yang valid
|
|
3. **Durasi pintu terbuka terlalu lama** (>180 detik)
|
|
4. **Akses tanpa ID card** di jam kerja normal
|
|
5. **ID card tidak valid** (format bukan EMP-XXXX)
|
|
|
|
### **Jam Kerja (Authorized Time):**
|
|
- **Senin - Jumat**: 07:00 - 18:00
|
|
- **Sabtu - Minggu**: Semua akses dianggap after_hours
|
|
|
|
### **Lokasi Pintu:**
|
|
- **front_door**: Pintu depan (akses utama)
|
|
- **back_door**: Pintu belakang (akses service)
|
|
- **side_door**: Pintu samping (akses sekunder)
|
|
- **main_entrance**: Pintu masuk utama gedung
|
|
|
|
### **Metode Akses:**
|
|
- **keycard**: Menggunakan kartu akses (paling aman)
|
|
- **manual**: Buka manual dengan kunci
|
|
- **force**: Paksa masuk (trigger alert)
|
|
- **emergency**: Akses darurat
|
|
- **maintenance**: Akses maintenance
|
|
- **unknown**: Metode tidak diketahui
|
|
|
|
## 🔔 **Sistem Notifikasi**
|
|
|
|
### **Priority Level:**
|
|
- **critical**: Forced entry (paksa masuk)
|
|
- **high**: Unauthorized access, after hours tanpa ID
|
|
- **medium**: Emergency access, suspicious activity
|
|
- **low**: Maintenance access
|
|
- **info**: Authorized access normal
|
|
|
|
### **Jenis Notifikasi:**
|
|
1. **📧 Email** - Semua alert
|
|
2. **📱 SMS** - Priority CRITICAL & HIGH (forced entry, unauthorized)
|
|
3. **🔔 Push Notification** - Real-time ke security app
|
|
4. **🔗 Webhook** - Ke sistem access control eksternal
|
|
5. **📝 Security Log** - Audit trail lengkap dengan user tracking
|
|
|
|
### **Contoh Notifikasi:**
|
|
```
|
|
🚨 SECURITY BREACH: PAKSA MASUK pada Reed Switch Pintu Utama di front_door
|
|
Durasi: 180s. Waktu: 2026-04-28 22:15:00
|
|
|
|
🚨 DOOR ALERT: Akses unauthorized pada Reed Switch Pintu Utama di front_door tanpa ID
|
|
Metode: manual. Durasi: 45s. Waktu: 2026-04-28 22:30:00
|
|
```
|
|
|
|
## 📊 **Database Schema**
|
|
|
|
### **door_access_readings table:**
|
|
```sql
|
|
- id (bigint, primary key)
|
|
- device_id (foreign key to devices)
|
|
- door_opened (boolean) - Status pintu terbuka
|
|
- is_authorized_access (boolean) - Akses sah/tidak
|
|
- access_type (enum) - Tipe akses
|
|
- access_method (enum) - Metode akses
|
|
- user_id_card (string) - ID card pengguna
|
|
- duration_seconds (integer) - Durasi pintu terbuka
|
|
- is_suspicious (boolean) - Akses mencurigakan
|
|
- door_location (enum) - Lokasi pintu
|
|
- is_forced_entry (boolean) - Paksa masuk
|
|
- metadata (json) - Data tambahan sensor
|
|
- door_opened_at (timestamp) - Waktu pintu dibuka
|
|
- door_closed_at (timestamp) - Waktu pintu ditutup
|
|
- recorded_at (timestamp) - Waktu pembacaan
|
|
- created_at, updated_at
|
|
```
|
|
|
|
## 🔌 **Integrasi IoT Device**
|
|
|
|
### **Arduino/ESP32 Example:**
|
|
```cpp
|
|
#include <WiFi.h>
|
|
#include <HTTPClient.h>
|
|
#include <ArduinoJson.h>
|
|
#include <RFID.h>
|
|
|
|
int reedSwitchPin = 2;
|
|
int rfidReaderPin = 4;
|
|
bool doorState = false;
|
|
unsigned long doorOpenTime = 0;
|
|
String lastIdCard = "";
|
|
|
|
void setup() {
|
|
pinMode(reedSwitchPin, INPUT_PULLUP);
|
|
Serial.begin(115200);
|
|
// WiFi setup...
|
|
// RFID setup...
|
|
}
|
|
|
|
void loop() {
|
|
bool currentDoorState = !digitalRead(reedSwitchPin); // Reed switch logic
|
|
|
|
if (currentDoorState != doorState) {
|
|
doorState = currentDoorState;
|
|
|
|
if (doorState) {
|
|
// Door opened
|
|
doorOpenTime = millis();
|
|
lastIdCard = readRFIDCard(); // Read ID card if available
|
|
Serial.println("Door opened, ID: " + lastIdCard);
|
|
|
|
} else {
|
|
// Door closed
|
|
unsigned long duration = (millis() - doorOpenTime) / 1000;
|
|
|
|
sendDoorAccessData(true, lastIdCard, duration, "front_door");
|
|
Serial.println("Door closed, data sent");
|
|
|
|
lastIdCard = "";
|
|
}
|
|
}
|
|
|
|
delay(100);
|
|
}
|
|
|
|
String readRFIDCard() {
|
|
// Implementasi baca RFID card
|
|
// Return format: "EMP-1234" atau "" jika tidak ada
|
|
return "EMP-1234"; // Simulasi
|
|
}
|
|
|
|
void sendDoorAccessData(bool opened, String idCard, int duration, String location) {
|
|
HTTPClient http;
|
|
http.begin("http://your-server.com/api/door-access/data");
|
|
http.addHeader("Content-Type", "application/json");
|
|
|
|
StaticJsonDocument<400> doc;
|
|
doc["device_id"] = 1;
|
|
doc["door_opened"] = opened;
|
|
doc["access_method"] = idCard.length() > 0 ? "keycard" : "manual";
|
|
doc["user_id_card"] = idCard.length() > 0 ? idCard : nullptr;
|
|
doc["duration_seconds"] = duration;
|
|
doc["door_location"] = location;
|
|
doc["is_forced_entry"] = (idCard.length() == 0 && duration > 10);
|
|
|
|
String jsonString;
|
|
serializeJson(doc, jsonString);
|
|
|
|
int httpResponseCode = http.POST(jsonString);
|
|
|
|
if (httpResponseCode > 0) {
|
|
String response = http.getString();
|
|
Serial.println("Response: " + response);
|
|
}
|
|
|
|
http.end();
|
|
}
|
|
```
|
|
|
|
### **Python Example (Raspberry Pi):**
|
|
```python
|
|
import requests
|
|
import json
|
|
import time
|
|
from datetime import datetime
|
|
import RPi.GPIO as GPIO
|
|
from mfrc522 import SimpleMFRC522
|
|
|
|
REED_SWITCH_PIN = 18
|
|
reader = SimpleMFRC522()
|
|
|
|
def setup_reed_switch():
|
|
GPIO.setmode(GPIO.BCM)
|
|
GPIO.setup(REED_SWITCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
|
|
|
def read_rfid_card():
|
|
try:
|
|
id, text = reader.read_no_block()
|
|
if id:
|
|
return f"EMP-{id % 10000:04d}" # Format ke EMP-XXXX
|
|
return None
|
|
except:
|
|
return None
|
|
|
|
def send_door_access_data(device_id, door_opened, id_card, duration, location):
|
|
url = "http://your-server.com/api/door-access/data"
|
|
|
|
access_method = "keycard" if id_card else "manual"
|
|
is_forced = not id_card and duration > 10
|
|
|
|
data = {
|
|
"device_id": device_id,
|
|
"door_opened": door_opened,
|
|
"access_method": access_method,
|
|
"user_id_card": id_card,
|
|
"duration_seconds": duration,
|
|
"door_location": location,
|
|
"is_forced_entry": is_forced,
|
|
"metadata": {
|
|
"timestamp": datetime.now().isoformat(),
|
|
"sensor_type": "Reed_Switch_Magnetic",
|
|
"gpio_pin": REED_SWITCH_PIN
|
|
}
|
|
}
|
|
|
|
response = requests.post(url, json=data)
|
|
return response.json()
|
|
|
|
def monitor_door_access():
|
|
door_open = False
|
|
door_open_time = None
|
|
current_id_card = None
|
|
|
|
while True:
|
|
# Read reed switch (False = door closed, True = door open)
|
|
door_state = not GPIO.input(REED_SWITCH_PIN)
|
|
|
|
if door_state and not door_open:
|
|
# Door just opened
|
|
door_open = True
|
|
door_open_time = time.time()
|
|
current_id_card = read_rfid_card()
|
|
print(f"Door opened, ID Card: {current_id_card}")
|
|
|
|
elif not door_state and door_open:
|
|
# Door just closed
|
|
door_open = False
|
|
duration = int(time.time() - door_open_time) if door_open_time else 0
|
|
|
|
result = send_door_access_data(1, True, current_id_card, duration, "front_door")
|
|
print(f"Door access data sent: {result}")
|
|
|
|
current_id_card = None
|
|
door_open_time = None
|
|
|
|
time.sleep(0.1)
|
|
|
|
# Usage
|
|
setup_reed_switch()
|
|
monitor_door_access()
|
|
```
|
|
|
|
## 🧪 **Testing API**
|
|
|
|
### **Test dengan cURL:**
|
|
```bash
|
|
# Test authorized access (jam kerja + ID card)
|
|
curl -X POST http://localhost:8000/api/door-access/data \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"device_id": 1,
|
|
"door_opened": true,
|
|
"access_method": "keycard",
|
|
"user_id_card": "EMP-1234",
|
|
"duration_seconds": 30,
|
|
"door_location": "front_door"
|
|
}'
|
|
|
|
# Test unauthorized access (tanpa ID card)
|
|
curl -X POST http://localhost:8000/api/door-access/data \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"device_id": 1,
|
|
"door_opened": true,
|
|
"access_method": "manual",
|
|
"duration_seconds": 60,
|
|
"door_location": "back_door"
|
|
}'
|
|
|
|
# Test forced entry (CRITICAL ALERT)
|
|
curl -X POST http://localhost:8000/api/door-access/data \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"device_id": 1,
|
|
"door_opened": true,
|
|
"access_method": "force",
|
|
"duration_seconds": 180,
|
|
"door_location": "front_door",
|
|
"is_forced_entry": true
|
|
}'
|
|
|
|
# Get statistics
|
|
curl "http://localhost:8000/api/door-access/statistics?device_id=1&hours=24"
|
|
|
|
# Test door access notification
|
|
curl -X POST http://localhost:8000/api/test-door-access-notification
|
|
```
|
|
|
|
## 📈 **Security Dashboard**
|
|
|
|
### **Real-time Monitoring:**
|
|
- Current door status (open/closed)
|
|
- Active access attempts
|
|
- Unauthorized access count (24h)
|
|
- Forced entry alerts
|
|
- User access patterns
|
|
|
|
### **Access Control Reports:**
|
|
- Daily access summary by user
|
|
- Unauthorized access attempts
|
|
- After-hours access log
|
|
- Door usage statistics by location
|
|
- ID card usage tracking
|
|
|
|
## 🔒 **Security Features**
|
|
|
|
### **Smart Access Control:**
|
|
- **ID Card Validation**: Format EMP-XXXX validation
|
|
- **Time-based Rules**: Working hours vs after hours
|
|
- **Duration Monitoring**: Alert untuk pintu terbuka terlalu lama
|
|
- **Forced Entry Detection**: Deteksi paksa masuk real-time
|
|
- **User Tracking**: Track akses per ID card
|
|
|
|
### **Multi-level Security:**
|
|
- **Immediate**: Push notification untuk forced entry
|
|
- **Escalation**: SMS untuk unauthorized access
|
|
- **Audit Trail**: Log lengkap semua akses
|
|
- **Integration**: Webhook ke sistem access control
|
|
|
|
## 🚀 **Deployment & Configuration**
|
|
|
|
### **Environment Variables:**
|
|
```env
|
|
# Door Access Settings
|
|
DOOR_ACCESS_WEBHOOK_URL=https://your-access-control.com/alerts
|
|
WORK_START_HOUR=7
|
|
WORK_END_HOUR=18
|
|
MAX_DOOR_OPEN_DURATION=180
|
|
|
|
# ID Card Settings
|
|
ID_CARD_FORMAT_REGEX=^EMP-\d{4}$
|
|
VALID_ID_CARDS=EMP-1234,EMP-5678,EMP-9012
|
|
|
|
# Notification settings
|
|
SECURITY_EMAIL=security@yourcompany.com
|
|
SECURITY_SMS_NUMBER=+6281234567890
|
|
```
|
|
|
|
### **Konfigurasi Access Control:**
|
|
```php
|
|
// config/door_access.php
|
|
return [
|
|
'work_hours' => [
|
|
'start' => 7, // 07:00
|
|
'end' => 18, // 18:00
|
|
'weekdays_only' => true
|
|
],
|
|
'thresholds' => [
|
|
'max_door_open_duration' => 180, // 3 minutes
|
|
'suspicious_duration' => 120, // 2 minutes
|
|
],
|
|
'id_card' => [
|
|
'format_regex' => '^EMP-\d{4}$',
|
|
'required_for_access' => true
|
|
],
|
|
'locations' => [
|
|
'front_door' => ['priority' => 'high', 'require_id' => true],
|
|
'back_door' => ['priority' => 'medium', 'require_id' => true],
|
|
'side_door' => ['priority' => 'medium', 'require_id' => false],
|
|
'main_entrance' => ['priority' => 'high', 'require_id' => true]
|
|
]
|
|
];
|
|
```
|
|
|
|
## 📞 **Troubleshooting**
|
|
|
|
### **Common Issues:**
|
|
1. **False alarms**: Adjust door open duration threshold
|
|
2. **ID card not detected**: Check RFID reader connection
|
|
3. **Missing alerts**: Verify notification service configuration
|
|
4. **Wrong access type**: Check working hours configuration
|
|
|
|
### **Monitoring Commands:**
|
|
```bash
|
|
# Check door access logs
|
|
tail -f storage/logs/laravel.log | grep "Door Access"
|
|
|
|
# Database cleanup
|
|
php artisan door-access:cleanup-old-data
|
|
|
|
# Test access control system
|
|
php artisan door-access:test-system
|
|
```
|
|
|
|
---
|
|
|
|
**Backend Smart Rack Door Access (Reed Switch) siap digunakan!** 🎉
|
|
|
|
Sistem akan otomatis:
|
|
- ✅ Menerima data sensor Reed Switch real-time
|
|
- ✅ Mendeteksi akses mencurigakan berdasarkan ID card, waktu, dan metode
|
|
- ✅ Membedakan akses sah vs tidak sah
|
|
- ✅ Mengirim notifikasi multi-channel untuk security breach
|
|
- ✅ Menyimpan audit trail lengkap untuk compliance
|
|
- ✅ Menyediakan statistik akses dan laporan keamanan
|
|
- ✅ Integrasi mudah dengan sistem access control dan CCTV
|
|
- ✅ Support multiple door locations dan access methods |