Compare commits

...

2 commits

Author SHA1 Message Date
Benjamin Nussbaum
1afd8935af
Revise download and star statistics in README
Some checks failed
tests / ci (push) Has been cancelled
Updated download and star counts for TRMNL BYOS Laravel.
2026-02-01 14:26:56 +01:00
Jamie Shiell
9578251238 Add maximum_compatibility boolean to devices to address redraw issues with certain hardware (#178) 2026-02-01 14:20:50 +01:00
6 changed files with 55 additions and 2 deletions

View file

@ -3,7 +3,7 @@
[![tests](https://github.com/usetrmnl/byos_laravel/actions/workflows/test.yml/badge.svg)](https://github.com/usetrmnl/byos_laravel/actions/workflows/test.yml)
TRMNL BYOS Laravel is a self-hostable implementation of a TRMNL server, built with Laravel.
It allows you to manage TRMNL devices, generate screens using **native plugins** (Screens API, Markup), **recipes** (120+ from the [OSS community catalog](https://bnussbau.github.io/trmnl-recipe-catalog/), 600+ from the [TRMNL catalog](https://trmnl.com/recipes), or your own), or the **API**, and can also act as a **proxy** for the native cloud service (Core). With over 40k downloads and 160+ stars, its the most popular community-driven BYOS.
It allows you to manage TRMNL devices, generate screens using **native plugins** (Screens API, Markup), **recipes** (130+ from the [OSS community catalog](https://bnussbau.github.io/trmnl-recipe-catalog/), 700+ from the [TRMNL catalog](https://trmnl.com/recipes), or your own), or the **API**, and can also act as a **proxy** for the native cloud service (Core). With over 50k downloads and 200+ stars, its the most popular community-driven BYOS.
![Screenshot](README_byos-screenshot.png)
![Screenshot](README_byos-screenshot-dark.png)

View file

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

View file

@ -194,6 +194,7 @@ Route::get('/display', function (Request $request) {
'update_firmware' => $device->update_firmware,
'firmware_url' => $device->firmware_url,
'special_function' => $device->special_function ?? 'sleep',
'maximum_compatibility' => $device->maximum_compatibility,
];
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,
'firmware_url' => null,
'special_function' => 'sleep',
'maximum_compatibility' => false,
]);
expect($device->fresh())
@ -95,6 +96,27 @@ test('display endpoint omits image_url_timeout when not configured', function ()
->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 {
$user = User::factory()->create(['assign_new_devices' => true]);