diff --git a/app/Models/Device.php b/app/Models/Device.php
index d4b5745..a57c0ee 100644
--- a/app/Models/Device.php
+++ b/app/Models/Device.php
@@ -4,6 +4,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\HasMany;
class Device extends Model
{
@@ -50,4 +51,29 @@ class Device extends Model
return 3; // Strong signal (3 bars)
}
}
+
+ public function playlists(): HasMany
+ {
+ return $this->hasMany(Playlist::class);
+ }
+
+ public function getNextPlaylistItem(): ?PlaylistItem
+ {
+ // Get all active playlists
+ $playlists = $this->playlists()
+ ->where('is_active', true)
+ ->get();
+
+ // Find the first active playlist with an available item
+ foreach ($playlists as $playlist) {
+ if ($playlist->isActiveNow()) {
+ $nextItem = $playlist->getNextPlaylistItem();
+ if ($nextItem) {
+ return $nextItem;
+ }
+ }
+ }
+
+ return null;
+ }
}
diff --git a/app/Models/Playlist.php b/app/Models/Playlist.php
new file mode 100644
index 0000000..c3744e9
--- /dev/null
+++ b/app/Models/Playlist.php
@@ -0,0 +1,94 @@
+ 'boolean',
+ 'weekdays' => 'array',
+ 'active_from' => 'datetime:H:i',
+ 'active_until' => 'datetime:H:i',
+ ];
+
+ public function device(): BelongsTo
+ {
+ return $this->belongsTo(Device::class);
+ }
+
+ public function items(): HasMany
+ {
+ return $this->hasMany(PlaylistItem::class);
+ }
+
+ public function isActiveNow(): bool
+ {
+ if (! $this->is_active) {
+ return false;
+ }
+
+ // Check weekday
+ if ($this->weekdays !== null) {
+ if (! in_array(now()->dayOfWeek, $this->weekdays)) {
+ return false;
+ }
+ }
+ // Check time range
+ if ($this->active_from !== null && $this->active_until !== null) {
+ if (! now()->between($this->active_from, $this->active_until)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function getNextPlaylistItem(): ?PlaylistItem
+ {
+ if (! $this->isActiveNow()) {
+ return null;
+ }
+
+ // Get active playlist items ordered by display order
+ $playlistItems = $this->items()
+ ->where('is_active', true)
+ ->orderBy('order')
+ ->get();
+
+ if ($playlistItems->isEmpty()) {
+ return null;
+ }
+
+ // Get the last displayed item
+ $lastDisplayed = $playlistItems
+ ->sortByDesc('last_displayed_at')
+ ->first();
+
+ if (! $lastDisplayed || ! $lastDisplayed->last_displayed_at) {
+ // If no item has been displayed yet, return the first one
+ return $playlistItems->first();
+ }
+
+ // Find the next item in sequence
+ $currentOrder = $lastDisplayed->order;
+ $nextItem = $playlistItems
+ ->where('order', '>', $currentOrder)
+ ->first();
+
+ // If there's no next item, loop back to the first one
+ if (! $nextItem) {
+ return $playlistItems->first();
+ }
+
+ return $nextItem;
+ }
+}
diff --git a/app/Models/PlaylistItem.php b/app/Models/PlaylistItem.php
new file mode 100644
index 0000000..4eba877
--- /dev/null
+++ b/app/Models/PlaylistItem.php
@@ -0,0 +1,29 @@
+ 'boolean',
+ 'last_displayed_at' => 'datetime',
+ ];
+
+ public function playlist(): BelongsTo
+ {
+ return $this->belongsTo(Playlist::class);
+ }
+
+ public function plugin(): BelongsTo
+ {
+ return $this->belongsTo(Plugin::class);
+ }
+}
diff --git a/app/Models/Plugin.php b/app/Models/Plugin.php
new file mode 100644
index 0000000..6042f23
--- /dev/null
+++ b/app/Models/Plugin.php
@@ -0,0 +1,29 @@
+ 'json',
+ ];
+
+ protected static function boot()
+ {
+ parent::boot();
+
+ static::creating(function ($model) {
+ if (empty($model->uuid)) {
+ $model->uuid = Str::uuid();
+ }
+ });
+ }
+}
diff --git a/database/factories/PlaylistFactory.php b/database/factories/PlaylistFactory.php
new file mode 100644
index 0000000..d955064
--- /dev/null
+++ b/database/factories/PlaylistFactory.php
@@ -0,0 +1,28 @@
+ $this->faker->randomNumber(),
+ 'is_active' => $this->faker->boolean(),
+ 'last_displayed_at' => Carbon::now(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+
+ 'device_id' => Device::factory(),
+ 'plugin_id' => Plugin::factory(),
+ ];
+ }
+}
diff --git a/database/factories/PlaylistItemFactory.php b/database/factories/PlaylistItemFactory.php
new file mode 100644
index 0000000..5e4fba5
--- /dev/null
+++ b/database/factories/PlaylistItemFactory.php
@@ -0,0 +1,28 @@
+ $this->faker->randomNumber(),
+ 'is_active' => $this->faker->boolean(),
+ 'last_displayed_at' => Carbon::now(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+
+ 'playlist_id' => Playlist::factory(),
+ 'plugin_id' => Plugin::factory(),
+ ];
+ }
+}
diff --git a/database/migrations/2025_03_12_151937_create_playlists_table.php b/database/migrations/2025_03_12_151937_create_playlists_table.php
new file mode 100644
index 0000000..2cc6570
--- /dev/null
+++ b/database/migrations/2025_03_12_151937_create_playlists_table.php
@@ -0,0 +1,33 @@
+id();
+ $table->foreignId('device_id')->constrained()->onDelete('cascade');
+ $table->string('name');
+ $table->boolean('is_active')->default(true);
+ $table->json('weekdays')->nullable(); // Array of weekday numbers (0-6)
+ $table->time('active_from')->nullable();
+ $table->time('active_until')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('playlists');
+ }
+};
diff --git a/database/migrations/2025_03_12_191757_create_playlist_items_table.php b/database/migrations/2025_03_12_191757_create_playlist_items_table.php
new file mode 100644
index 0000000..366d8ae
--- /dev/null
+++ b/database/migrations/2025_03_12_191757_create_playlist_items_table.php
@@ -0,0 +1,32 @@
+id();
+ $table->foreignId('playlist_id')->constrained()->onDelete('cascade');
+ $table->foreignId('plugin_id')->constrained()->onDelete('cascade');
+ $table->integer('order')->default(0);
+ $table->boolean('is_active')->default(true);
+ $table->timestamp('last_displayed_at')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('playlist_items');
+ }
+};
diff --git a/resources/views/components/layouts/app/header.blade.php b/resources/views/components/layouts/app/header.blade.php
index 513798d..18ff125 100644
--- a/resources/views/components/layouts/app/header.blade.php
+++ b/resources/views/components/layouts/app/header.blade.php
@@ -17,11 +17,11 @@
Dashboard
{{$device->mac_address}}
- @if($current_image_uuid) + @if($current_image_path)