333 lines
11 KiB
PHP
333 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Room;
|
|
use App\Services\MqttService;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use App\Models\Announcement;
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
class AnnouncementController extends Controller
|
|
{
|
|
protected $mqttService;
|
|
|
|
public function __construct(MqttService $mqttService)
|
|
{
|
|
$this->mqttService = $mqttService;
|
|
}
|
|
|
|
/**
|
|
* Display the announcement interface
|
|
*/
|
|
public function index()
|
|
{
|
|
$rooms = Room::pluck('name')->toArray();
|
|
$activeAnnouncements = Announcement::where('is_active', true)->get();
|
|
$announcementHistory = Announcement::where('is_active', false)
|
|
->orderBy('sent_at', 'desc')
|
|
->paginate(10);
|
|
|
|
if (empty($rooms)) {
|
|
$rooms = ['ruang1', 'ruang2', 'ruang3'];
|
|
}
|
|
|
|
return view('admin.announcements.index', compact('rooms', 'activeAnnouncements', 'announcementHistory'));
|
|
}
|
|
|
|
/**
|
|
* Get MQTT connection status
|
|
*/
|
|
public function mqttStatus()
|
|
{
|
|
return response()->json([
|
|
'connected' => $this->mqttService->isConnected(),
|
|
'status' => Cache::get('mqtt_status', 'disconnected')
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Check for active announcements
|
|
*/
|
|
public function checkActive()
|
|
{
|
|
$active = Cache::get('active_announcement', false);
|
|
|
|
return response()->json([
|
|
'active' => $active,
|
|
'type' => $active ? Cache::get('active_announcement_type') : null,
|
|
'duration' => $active ? Cache::get('active_announcement_duration') : null,
|
|
'remaining' => $active ? (Cache::get('active_announcement_end') - time()) : null
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get active announcements
|
|
*/
|
|
public function activeAnnouncements()
|
|
{
|
|
$announcements = Announcement::where('is_active', true)
|
|
->orderBy('sent_at', 'desc')
|
|
->get();
|
|
|
|
return response()->json($announcements);
|
|
}
|
|
|
|
/**
|
|
* Get announcement history
|
|
*/
|
|
public function announcementHistory(Request $request)
|
|
{
|
|
$query = Announcement::where('is_active', false)
|
|
->orderBy('sent_at', 'desc');
|
|
|
|
if ($request->has('search')) {
|
|
$query->where('content', 'like', '%'.$request->search.'%');
|
|
}
|
|
|
|
if ($request->has('filter') && $request->filter != 'all') {
|
|
$query->where('type', $request->filter);
|
|
}
|
|
|
|
if ($request->has('sort')) {
|
|
$query->orderBy('sent_at', $request->sort == 'oldest' ? 'asc' : 'desc');
|
|
}
|
|
|
|
return response()->json($query->paginate(10));
|
|
}
|
|
|
|
/**
|
|
* Get announcement details
|
|
*/
|
|
public function announcementDetails($id)
|
|
{
|
|
$announcement = Announcement::findOrFail($id);
|
|
return response()->json($announcement);
|
|
}
|
|
|
|
/**
|
|
* Send announcement to MQTT
|
|
*/
|
|
public function send(Request $request)
|
|
{
|
|
$validated = $request->validate([
|
|
'type' => 'required|in:tts,manual',
|
|
'content' => 'nullable|required_if:type,tts|string|max:500',
|
|
'rooms' => 'required|array|min:1',
|
|
'rooms.*' => 'string',
|
|
'duration' => 'nullable|integer|min:5|max:300',
|
|
]);
|
|
|
|
try {
|
|
$announcement = Announcement::create([
|
|
'type' => $validated['type'],
|
|
'content' => $validated['type'] === 'tts' ? $validated['content'] : null,
|
|
'target_rooms' => $validated['rooms'],
|
|
'duration' => $validated['type'] === 'manual' ? $validated['duration'] : null,
|
|
'sent_at' => now(),
|
|
'is_active' => true,
|
|
'status' => 'processing'
|
|
]);
|
|
|
|
$payload = [
|
|
'type' => $validated['type'],
|
|
'target_rooms' => $validated['rooms'],
|
|
'timestamp' => now()->toDateTimeString(),
|
|
'announcement_id' => $announcement->id
|
|
];
|
|
|
|
if ($validated['type'] === 'tts') {
|
|
$payload['content'] = $validated['content'];
|
|
$payload['audio_url'] = $this->generateTtsAudio($validated['content']);
|
|
$payload['auto_stop'] = true;
|
|
} else {
|
|
$payload['duration'] = $validated['duration'] ?? 60;
|
|
}
|
|
|
|
$this->mqttService->sendAnnouncement($payload);
|
|
|
|
// Update cache for active announcement
|
|
if ($validated['type'] === 'manual') {
|
|
Cache::put('active_announcement', true);
|
|
Cache::put('active_announcement_type', 'manual');
|
|
Cache::put('active_announcement_duration', $validated['duration']);
|
|
Cache::put('active_announcement_end', time() + $validated['duration']);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Pengumuman berhasil dikirim!',
|
|
'announcement_id' => $announcement->id,
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error('Announcement error: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Gagal mengirim pengumuman: ' . $e->getMessage(),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop manual audio routing
|
|
*/
|
|
public function stopManual()
|
|
{
|
|
try {
|
|
Announcement::where('is_active', true)
|
|
->update([
|
|
'is_active' => false,
|
|
'status' => 'completed'
|
|
]);
|
|
|
|
$payload = [
|
|
'type' => 'stop_manual',
|
|
'timestamp' => now()->toDateTimeString(),
|
|
];
|
|
|
|
$this->mqttService->publish('bel/sekolah/pengumuman', json_encode($payload), 1, false);
|
|
|
|
// Clear active announcement cache
|
|
Cache::forget('active_announcement');
|
|
Cache::forget('active_announcement_type');
|
|
Cache::forget('active_announcement_duration');
|
|
Cache::forget('active_announcement_end');
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Routing audio berhasil diputus!',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error('Stop manual error: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Gagal memutus routing audio: ' . $e->getMessage(),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop specific announcement
|
|
*/
|
|
public function stopAnnouncement(Request $request)
|
|
{
|
|
try {
|
|
$announcement = Announcement::findOrFail($request->id);
|
|
$announcement->update([
|
|
'is_active' => false,
|
|
'status' => 'stopped'
|
|
]);
|
|
|
|
if ($announcement->type === 'manual') {
|
|
$payload = [
|
|
'type' => 'stop_manual',
|
|
'timestamp' => now()->toDateTimeString(),
|
|
];
|
|
$this->mqttService->publish('bel/sekolah/pengumuman', json_encode($payload), 1, false);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Pengumuman berhasil dihentikan!',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error('Stop announcement error: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Gagal menghentikan pengumuman: ' . $e->getMessage(),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Manage rooms (add, edit, delete)
|
|
*/
|
|
public function manageRoom(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'action' => 'required|in:add,edit,delete',
|
|
'room_name' => 'required|string|max:50',
|
|
'old_room' => 'nullable|string|max:50'
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => $validator->errors()->first()
|
|
], 400);
|
|
}
|
|
|
|
try {
|
|
switch ($request->action) {
|
|
case 'add':
|
|
Room::create(['name' => $request->room_name]);
|
|
break;
|
|
|
|
case 'edit':
|
|
$room = Room::where('name', $request->old_room)->firstOrFail();
|
|
$room->update(['name' => $request->room_name]);
|
|
|
|
// Update existing announcements that use this room
|
|
Announcement::whereJsonContains('target_rooms', $request->old_room)
|
|
->each(function($announcement) use ($request) {
|
|
$updatedRooms = array_map(function($room) use ($request) {
|
|
return $room == $request->old_room ? $request->room_name : $room;
|
|
}, $announcement->target_rooms);
|
|
|
|
$announcement->update(['target_rooms' => $updatedRooms]);
|
|
});
|
|
break;
|
|
|
|
case 'delete':
|
|
$room = Room::where('name', $request->room_name)->firstOrFail();
|
|
$room->delete();
|
|
break;
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Ruangan berhasil di' . ($request->action == 'add' ? 'tambah' : ($request->action == 'edit' ? 'edit' : 'hapus')),
|
|
'rooms' => Room::pluck('name')->toArray()
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error('Room management error: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Gagal mengelola ruangan: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate TTS audio using VoiceRSS
|
|
*/
|
|
private function generateTtsAudio($text)
|
|
{
|
|
$apiKey = config('services.voicerss.api_key');
|
|
|
|
if (!$apiKey) {
|
|
throw new \Exception('VoiceRSS API Key tidak dikonfigurasi');
|
|
}
|
|
|
|
$response = Http::timeout(10)->get('https://api.voicerss.org', [
|
|
'key' => $apiKey,
|
|
'hl' => 'id-id',
|
|
'src' => $text,
|
|
'r' => '0',
|
|
'c' => 'mp3',
|
|
'f' => '44khz_16bit_stereo',
|
|
]);
|
|
|
|
if ($response->successful()) {
|
|
$filename = 'tts/' . uniqid() . '.mp3';
|
|
Storage::disk('public')->put($filename, $response->body());
|
|
return Storage::url($filename);
|
|
}
|
|
|
|
throw new \Exception('Gagal menghasilkan audio TTS');
|
|
}
|
|
} |