Add maximum_compatibility boolean to devices to address redraw issues with certain hardware (#178)

This commit is contained in:
Jamie Shiell 2026-02-01 12:30:30 +00:00
parent bcfc62c782
commit 909ff967c7
No known key found for this signature in database
5 changed files with 54 additions and 1 deletions

View file

@ -42,6 +42,7 @@ class Device extends Model
'sleep_mode_to' => 'datetime:H:i', 'sleep_mode_to' => 'datetime:H:i',
'special_function' => 'string', 'special_function' => 'string',
'pause_until' => 'datetime', 'pause_until' => 'datetime',
'maximum_compatibility' => 'boolean',
]; ];
public function getBatteryPercentAttribute(): int|float public function getBatteryPercentAttribute(): int|float

View file

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table("devices", function (Blueprint $table): void {
$table->boolean("maximum_compatibility")->default(false);
});
}
public function down(): void
{
Schema::table("devices", function (Blueprint $table): void {
$table->dropColumn("maximum_compatibility");
});
}
};

View file

@ -31,6 +31,9 @@ new class extends Component
public $device_model_id; public $device_model_id;
// Signal to device to use high compatibility approaches when redrawing content
public $maximum_compatibility = false;
// Sleep mode and special function // Sleep mode and special function
public $sleep_mode_enabled = false; public $sleep_mode_enabled = false;
@ -81,6 +84,7 @@ new class extends Component
$this->rotate = $device->rotate; $this->rotate = $device->rotate;
$this->image_format = $device->image_format; $this->image_format = $device->image_format;
$this->device_model_id = $device->device_model_id; $this->device_model_id = $device->device_model_id;
$this->maximum_compatibility = $device->maximum_compatibility;
$this->deviceModels = DeviceModel::orderBy('label')->get()->sortBy(function ($deviceModel) { $this->deviceModels = DeviceModel::orderBy('label')->get()->sortBy(function ($deviceModel) {
// Put TRMNL models at the top, then sort alphabetically within each group // Put TRMNL models at the top, then sort alphabetically within each group
$isTrmnl = str_starts_with($deviceModel->label, 'TRMNL'); $isTrmnl = str_starts_with($deviceModel->label, 'TRMNL');
@ -141,6 +145,7 @@ new class extends Component
'rotate' => 'required|integer|min:0|max:359', 'rotate' => 'required|integer|min:0|max:359',
'image_format' => 'required|string', 'image_format' => 'required|string',
'device_model_id' => 'nullable|exists:device_models,id', 'device_model_id' => 'nullable|exists:device_models,id',
'maximum_compatibility' => 'boolean',
'sleep_mode_enabled' => 'boolean', 'sleep_mode_enabled' => 'boolean',
'sleep_mode_from' => 'nullable|date_format:H:i', 'sleep_mode_from' => 'nullable|date_format:H:i',
'sleep_mode_to' => 'nullable|date_format:H:i', 'sleep_mode_to' => 'nullable|date_format:H:i',
@ -160,6 +165,7 @@ new class extends Component
'rotate' => $this->rotate, 'rotate' => $this->rotate,
'image_format' => $this->image_format, 'image_format' => $this->image_format,
'device_model_id' => $deviceModelId, 'device_model_id' => $deviceModelId,
'maximum_compatibility' => $this->maximum_compatibility,
'sleep_mode_enabled' => $this->sleep_mode_enabled, 'sleep_mode_enabled' => $this->sleep_mode_enabled,
'sleep_mode_from' => $this->sleep_mode_from, 'sleep_mode_from' => $this->sleep_mode_from,
'sleep_mode_to' => $this->sleep_mode_to, 'sleep_mode_to' => $this->sleep_mode_to,
@ -427,6 +433,8 @@ new class extends Component
@endforeach @endforeach
</flux:select> </flux:select>
<flux:checkbox wire:model="maximum_compatibility" label="Maximum Compatibility" description="Enable if experiencing display issues" />
@if(empty($device_model_id)) @if(empty($device_model_id))
<flux:separator class="my-4" text="Advanced Device Settings" /> <flux:separator class="my-4" text="Advanced Device Settings" />
<div class="flex gap-4"> <div class="flex gap-4">
@ -787,4 +795,3 @@ new class extends Component
</div> </div>
</div> </div>
</div> </div>

View file

@ -194,6 +194,7 @@ Route::get('/display', function (Request $request) {
'update_firmware' => $device->update_firmware, 'update_firmware' => $device->update_firmware,
'firmware_url' => $device->firmware_url, 'firmware_url' => $device->firmware_url,
'special_function' => $device->special_function ?? 'sleep', 'special_function' => $device->special_function ?? 'sleep',
'maximum_compatibility' => $device->maximum_compatibility,
]; ];
if (config('services.trmnl.image_url_timeout')) { if (config('services.trmnl.image_url_timeout')) {

View file

@ -45,6 +45,7 @@ test('device can fetch display data with valid credentials', function (): void {
'update_firmware' => false, 'update_firmware' => false,
'firmware_url' => null, 'firmware_url' => null,
'special_function' => 'sleep', 'special_function' => 'sleep',
'maximum_compatibility' => false,
]); ]);
expect($device->fresh()) expect($device->fresh())
@ -95,6 +96,27 @@ test('display endpoint omits image_url_timeout when not configured', function ()
->assertJsonMissing(['image_url_timeout']); ->assertJsonMissing(['image_url_timeout']);
}); });
test('display endpoint includes maximum_compatibility value when true for device', function (): void {
$device = Device::factory()->create([
'mac_address' => '00:11:22:33:44:55',
'api_key' => 'test-api-key',
'maximum_compatibility' => true
]);
$response = $this->withHeaders([
'id' => $device->mac_address,
'access-token' => $device->api_key,
'rssi' => -70,
'battery_voltage' => 3.8,
'fw-version' => '1.0.0',
])->get('/api/display');
$response->assertOk()
->assertJson([
'maximum_compatibility' => true,
]);
});
test('new device is auto-assigned to user with auto-assign enabled', function (): void { test('new device is auto-assigned to user with auto-assign enabled', function (): void {
$user = User::factory()->create(['assign_new_devices' => true]); $user = User::factory()->create(['assign_new_devices' => true]);