diff --git a/app/Services/ImageGenerationService.php b/app/Services/ImageGenerationService.php
index a7bd3c6..d0ecddc 100644
--- a/app/Services/ImageGenerationService.php
+++ b/app/Services/ImageGenerationService.php
@@ -353,20 +353,27 @@ class ImageGenerationService
public static function resetIfNotCacheable(?Plugin $plugin): void
{
if ($plugin?->id) {
- // Check if any devices have custom dimensions or use DeviceModels
+ // Check if any devices have custom dimensions or use non-standard DeviceModels
$hasCustomDimensions = Device::query()
->where(function ($query) {
$query->where('width', '!=', 800)
->orWhere('height', '!=', 480)
->orWhere('rotate', '!=', 0);
})
- ->orWhereNotNull('device_model_id')
+ ->orWhereHas('deviceModel', function ($query) {
+ // Only allow caching if all device models have standard dimensions (800x480, rotation=0)
+ $query->where(function ($subQuery) {
+ $subQuery->where('width', '!=', 800)
+ ->orWhere('height', '!=', 480)
+ ->orWhere('rotation', '!=', 0);
+ });
+ })
->exists();
if ($hasCustomDimensions) {
// TODO cache image per device
$plugin->update(['current_image' => null]);
- Log::debug('Skip cache as devices with custom dimensions or DeviceModels exist');
+ Log::debug('Skip cache as devices with custom dimensions or non-standard DeviceModels exist');
}
}
}
diff --git a/routes/api.php b/routes/api.php
index c59b539..5f2a9f3 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -126,10 +126,25 @@ Route::get('/display', function (Request $request) {
$image_path = 'images/setup-logo.bmp';
$filename = 'setup-logo.bmp';
} else {
- if (isset($device->last_firmware_version)
- && version_compare($device->last_firmware_version, '1.5.2', '<')
- && Storage::disk('public')->exists('images/generated/'.$image_uuid.'.bmp')) {
- $image_path = 'images/generated/'.$image_uuid.'.bmp';
+ // Determine image format based on device settings
+ $preferred_format = 'png'; // Default to PNG for newer firmware
+
+ if (! $device->device_model_id) {
+ // No device model, use device's image_format setting
+ if (str_contains($device->image_format, 'bmp')) {
+ $preferred_format = 'bmp';
+ }
+ // For 'auto' or unknown formats, fall back to firmware version logic
+ if (isset($device->last_firmware_version)
+ && version_compare($device->last_firmware_version, '1.5.2', '<')
+ && Storage::disk('public')->exists('images/generated/'.$image_uuid.'.bmp')) {
+ $preferred_format = 'bmp';
+ }
+ }
+
+ // Check if a preferred format exists, otherwise fall back
+ if (Storage::disk('public')->exists('images/generated/'.$image_uuid.'.'.$preferred_format)) {
+ $image_path = 'images/generated/'.$image_uuid.'.'.$preferred_format;
} elseif (Storage::disk('public')->exists('images/generated/'.$image_uuid.'.png')) {
$image_path = 'images/generated/'.$image_uuid.'.png';
} else {
@@ -422,10 +437,25 @@ Route::get('/current_screen', function (Request $request) {
$image_path = 'images/setup-logo.bmp';
$filename = 'setup-logo.bmp';
} else {
- if (isset($device->last_firmware_version)
- && version_compare($device->last_firmware_version, '1.5.2', '<')
- && Storage::disk('public')->exists('images/generated/'.$image_uuid.'.bmp')) {
- $image_path = 'images/generated/'.$image_uuid.'.bmp';
+ // Determine image format based on device settings
+ $preferred_format = 'png'; // Default to PNG for newer firmware
+
+ if (! $device->device_model_id) {
+ // No device model, use device's image_format setting
+ if (str_contains($device->image_format, 'bmp')) {
+ $preferred_format = 'bmp';
+ }
+ // For 'auto' or unknown formats, fall back to firmware version logic
+ if (isset($device->last_firmware_version)
+ && version_compare($device->last_firmware_version, '1.5.2', '<')
+ && Storage::disk('public')->exists('images/generated/'.$image_uuid.'.bmp')) {
+ $preferred_format = 'bmp';
+ }
+ }
+
+ // Check if preferred format exists, otherwise fall back
+ if (Storage::disk('public')->exists('images/generated/'.$image_uuid.'.'.$preferred_format)) {
+ $image_path = 'images/generated/'.$image_uuid.'.'.$preferred_format;
} elseif (Storage::disk('public')->exists('images/generated/'.$image_uuid.'.png')) {
$image_path = 'images/generated/'.$image_uuid.'.png';
} else {
diff --git a/tests/Feature/Api/DeviceImageFormatTest.php b/tests/Feature/Api/DeviceImageFormatTest.php
index ff24744..fcb7555 100644
--- a/tests/Feature/Api/DeviceImageFormatTest.php
+++ b/tests/Feature/Api/DeviceImageFormatTest.php
@@ -1,6 +1,11 @@
'test-image.bmp',
]);
});
+
+test('device without device_model_id and image_format bmp3_1bit_srgb returns bmp when plugin is rendered', function () {
+ // Create a user with auto-assign enabled
+ $user = User::factory()->create([
+ 'assign_new_devices' => true,
+ ]);
+
+ // Create a device without device_model_id and with bmp3_1bit_srgb format
+ $device = Device::factory()->create([
+ 'user_id' => $user->id,
+ 'mac_address' => '00:11:22:33:44:55',
+ 'api_key' => 'test-api-key',
+ 'device_model_id' => null, // Explicitly set to null
+ 'image_format' => ImageFormat::BMP3_1BIT_SRGB->value,
+ 'last_firmware_version' => '1.5.2',
+ ]);
+
+ // Create a plugin
+ $plugin = Plugin::factory()->create([
+ 'user_id' => $user->id,
+ 'name' => 'Test Plugin',
+ 'render_markup' => '
Test Content
',
+ 'data_strategy' => 'static',
+ 'markup_language' => 'blade',
+ 'current_image' => 'test-generated-image', // Set current image directly
+ ]);
+
+ // Create a playlist for the device
+ $playlist = Playlist::factory()->create([
+ 'device_id' => $device->id,
+ 'is_active' => true,
+ 'refresh_time' => 900,
+ ]);
+
+ // Create a playlist item with the plugin
+ $playlistItem = PlaylistItem::factory()->create([
+ 'playlist_id' => $playlist->id,
+ 'plugin_id' => $plugin->id,
+ 'is_active' => true,
+ 'order' => 1,
+ ]);
+
+ // Mock the image generation to create both bmp and png files
+ $imageUuid = 'test-generated-image';
+ Storage::disk('public')->put('images/generated/'.$imageUuid.'.bmp', 'fake bmp content');
+ Storage::disk('public')->put('images/generated/'.$imageUuid.'.png', 'fake png content');
+
+ // Set the device's current screen image to the plugin's image
+ $device->update(['current_screen_image' => $imageUuid]);
+
+ // Test /api/display endpoint
+ $displayResponse = $this->withHeaders([
+ 'id' => $device->mac_address,
+ 'access-token' => $device->api_key,
+ 'rssi' => -70,
+ 'battery_voltage' => 3.8,
+ 'fw-version' => '1.5.2',
+ ])->get('/api/current_screen');
+
+ $displayResponse->assertOk();
+ $displayResponse->assertJson([
+ 'filename' => $imageUuid.'.bmp',
+ ]);
+
+ // Verify that the device's image_format is correctly set
+ $device->refresh();
+ expect($device->image_format)->toBe(ImageFormat::BMP3_1BIT_SRGB->value)
+ ->and($device->device_model_id)->toBeNull();
+});
diff --git a/tests/Feature/ImageGenerationServiceTest.php b/tests/Feature/ImageGenerationServiceTest.php
index 22699c5..fc06a5b 100644
--- a/tests/Feature/ImageGenerationServiceTest.php
+++ b/tests/Feature/ImageGenerationServiceTest.php
@@ -423,3 +423,35 @@ it('determines correct image format from device model', function (): void {
expect($device3->current_screen_image)->toBe($uuid3);
Storage::disk('public')->assertExists("/images/generated/{$uuid3}.png");
})->skipOnGitHubActions();
+
+it('generates BMP for legacy device with bmp3_1bit_srgb format', function (): void {
+ // Create a device with BMP format but no DeviceModel (legacy behavior)
+ $device = Device::factory()->create([
+ 'width' => 800,
+ 'height' => 480,
+ 'rotate' => 0,
+ 'image_format' => ImageFormat::BMP3_1BIT_SRGB->value,
+ 'device_model_id' => null, // Explicitly no DeviceModel
+ ]);
+
+ $markup = 'Test Content
';
+ $uuid = ImageGenerationService::generateImage($markup, $device->id);
+
+ // Assert the device was updated with a new image UUID
+ $device->refresh();
+ expect($device->current_screen_image)->toBe($uuid);
+
+ // Assert BMP file was created
+ Storage::disk('public')->assertExists("/images/generated/{$uuid}.bmp");
+
+ // Verify the BMP file has content and isn't blank
+ $imagePath = Storage::disk('public')->path("/images/generated/{$uuid}.bmp");
+ $imageSize = filesize($imagePath);
+ expect($imageSize)->toBeGreaterThan(100); // Should be at least 100 bytes for a BMP
+
+ // Verify it's a valid BMP file
+ $imageInfo = getimagesize($imagePath);
+ expect($imageInfo[0])->toBe(800); // Width
+ expect($imageInfo[1])->toBe(480); // Height
+ expect($imageInfo[2])->toBe(IMAGETYPE_BMP); // BMP type
+})->skipOnGitHubActions();
diff --git a/tests/Unit/Services/ImageGenerationServiceTest.php b/tests/Unit/Services/ImageGenerationServiceTest.php
index 4941c3c..b254243 100644
--- a/tests/Unit/Services/ImageGenerationServiceTest.php
+++ b/tests/Unit/Services/ImageGenerationServiceTest.php
@@ -226,6 +226,59 @@ it('reset_if_not_cacheable preserves cache for standard devices', function (): v
expect($plugin->current_image)->toBe('test-uuid');
})->skipOnGitHubActions();
+it('reset_if_not_cacheable preserves cache for og_png and og_plus device models', function (): void {
+ // Create a plugin
+ $plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
+
+ // Create og_png device model
+ $ogPngModel = DeviceModel::factory()->create([
+ 'name' => 'test_og_png',
+ 'width' => 800,
+ 'height' => 480,
+ 'rotation' => 0,
+ ]);
+
+ // Create og_plus device model
+ $ogPlusModel = DeviceModel::factory()->create([
+ 'name' => 'test_og_plus',
+ 'width' => 800,
+ 'height' => 480,
+ 'rotation' => 0,
+ ]);
+
+ // Create devices with og_png and og_plus device models
+ Device::factory()->create(['device_model_id' => $ogPngModel->id]);
+ Device::factory()->create(['device_model_id' => $ogPlusModel->id]);
+
+ // Test that the method preserves cache for standard device models
+ ImageGenerationService::resetIfNotCacheable($plugin);
+
+ $plugin->refresh();
+ expect($plugin->current_image)->toBe('test-uuid');
+})->skipOnGitHubActions();
+
+it('reset_if_not_cacheable resets cache for non-standard device models', function (): void {
+ // Create a plugin
+ $plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
+
+ // Create a non-standard device model (e.g., kindle)
+ $kindleModel = DeviceModel::factory()->create([
+ 'name' => 'test_amazon_kindle_2024',
+ 'width' => 1400,
+ 'height' => 840,
+ 'rotation' => 90,
+ ]);
+
+ // Create a device with the non-standard device model
+ Device::factory()->create(['device_model_id' => $kindleModel->id]);
+
+ // Test that the method resets cache for non-standard device models
+ ImageGenerationService::resetIfNotCacheable($plugin);
+
+ $plugin->refresh();
+ expect($plugin->current_image)->toBeNull();
+})->skipOnGitHubActions();
+
it('reset_if_not_cacheable handles null plugin', function (): void {
// Test that the method handles null plugin gracefully
expect(fn () => ImageGenerationService::resetIfNotCacheable(null))->not->toThrow(Exception::class);