mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-03-14 12:23:33 +00:00
chore: update trmnl base url
This commit is contained in:
parent
31ca919ba9
commit
1e43aded77
16 changed files with 109 additions and 73 deletions
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
|
@ -1 +1 @@
|
||||||
custom: ["https://usetrmnl.com/?ref=laravel-trmnl"]
|
custom: ["https://trmnl.com/?ref=laravel-trmnl"]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
[](https://github.com/usetrmnl/byos_laravel/actions/workflows/test.yml)
|
[](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.
|
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://usetrmnl.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, it’s the most popular community-driven BYOS.
|
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, it’s the most popular community-driven BYOS.
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
@ -15,9 +15,9 @@ It allows you to manage TRMNL devices, generate screens using **native plugins**
|
||||||
* 📡 Device Information – Display battery status, WiFi strength, firmware version, and more.
|
* 📡 Device Information – Display battery status, WiFi strength, firmware version, and more.
|
||||||
* 🔍 Auto-Join – Automatically detects and adds devices from your local network.
|
* 🔍 Auto-Join – Automatically detects and adds devices from your local network.
|
||||||
* 🖥️ Screen Generation – Supports Plugins (including Mashups), Recipes, API, Markup, or updates via Code.
|
* 🖥️ Screen Generation – Supports Plugins (including Mashups), Recipes, API, Markup, or updates via Code.
|
||||||
* Support for TRMNL [Design Framework](https://usetrmnl.com/framework)
|
* Support for TRMNL [Design Framework](https://trmnl.com/framework)
|
||||||
* Compatible open-source recipes are available in the [community catalog](https://bnussbau.github.io/trmnl-recipe-catalog/)
|
* Compatible open-source recipes are available in the [community catalog](https://bnussbau.github.io/trmnl-recipe-catalog/)
|
||||||
* Import from the [TRMNL community recipe catalog](https://usetrmnl.com/recipes)
|
* Import from the [TRMNL community recipe catalog](https://trmnl.com/recipes)
|
||||||
* Supported Devices
|
* Supported Devices
|
||||||
* TRMNL OG (1-bit & 2-bit)
|
* TRMNL OG (1-bit & 2-bit)
|
||||||
* SeeedStudio TRMNL 7,5" (OG) DIY Kit
|
* SeeedStudio TRMNL 7,5" (OG) DIY Kit
|
||||||
|
|
@ -43,7 +43,7 @@ It allows you to manage TRMNL devices, generate screens using **native plugins**
|
||||||
### Support ❤️
|
### Support ❤️
|
||||||
This repo is maintained voluntarily by [@bnussbau](https://github.com/bnussbau).
|
This repo is maintained voluntarily by [@bnussbau](https://github.com/bnussbau).
|
||||||
|
|
||||||
Support the development of this package by purchasing a TRMNL device through the referral link: https://usetrmnl.com/?ref=laravel-trmnl. At checkout, use the code `laravel-trmnl` to receive a $15 discount on your purchase.
|
Support the development of this package by purchasing a TRMNL device through the referral link: https://trmnl.com/?ref=laravel-trmnl. At checkout, use the code `laravel-trmnl` to receive a $15 discount on your purchase.
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ final class FetchDeviceModelsJob implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
private const API_URL = 'https://usetrmnl.com/api/models';
|
private const API_URL = '/api/models';
|
||||||
|
|
||||||
private const PALETTES_API_URL = 'http://usetrmnl.com/api/palettes';
|
private const PALETTES_API_URL = 'http://usetrmnl.com/api/palettes';
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ final class FetchDeviceModelsJob implements ShouldQueue
|
||||||
try {
|
try {
|
||||||
$this->processPalettes();
|
$this->processPalettes();
|
||||||
|
|
||||||
$response = Http::timeout(30)->get(self::API_URL);
|
$response = Http::timeout(30)->get(config('services.trmnl.base_url').self::API_URL);
|
||||||
|
|
||||||
if (! $response->successful()) {
|
if (! $response->successful()) {
|
||||||
Log::error('Failed to fetch device models from API', [
|
Log::error('Failed to fetch device models from API', [
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,9 @@ class FirmwarePollJob implements ShouldQueue
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$response = Http::get('https://usetrmnl.com/api/firmware/latest')->json();
|
$firmwareEndpoint = config('services.trmnl.base_url').'/api/firmware/latest';
|
||||||
|
|
||||||
|
$response = Http::get($firmwareEndpoint)->json();
|
||||||
|
|
||||||
if (! is_array($response) || ! isset($response['version']) || ! isset($response['url'])) {
|
if (! is_array($response) || ! isset($response['version']) || ! isset($response['url'])) {
|
||||||
Log::error('Invalid firmware response format received');
|
Log::error('Invalid firmware response format received');
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ return [
|
||||||
],
|
],
|
||||||
|
|
||||||
'trmnl' => [
|
'trmnl' => [
|
||||||
|
'base_url' => 'https://trmnl.com',
|
||||||
'proxy_base_url' => env('TRMNL_PROXY_BASE_URL', 'https://trmnl.app'),
|
'proxy_base_url' => env('TRMNL_PROXY_BASE_URL', 'https://trmnl.app'),
|
||||||
'proxy_refresh_minutes' => env('TRMNL_PROXY_REFRESH_MINUTES', 15),
|
'proxy_refresh_minutes' => env('TRMNL_PROXY_REFRESH_MINUTES', 15),
|
||||||
'proxy_refresh_cron' => env('TRMNL_PROXY_REFRESH_CRON'),
|
'proxy_refresh_cron' => env('TRMNL_PROXY_REFRESH_CRON'),
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,13 @@ class extends Component
|
||||||
try {
|
try {
|
||||||
$cacheKey = 'trmnl_recipes_newest_page_'.$this->page;
|
$cacheKey = 'trmnl_recipes_newest_page_'.$this->page;
|
||||||
$response = Cache::remember($cacheKey, 43200, function () {
|
$response = Cache::remember($cacheKey, 43200, function () {
|
||||||
$response = Http::timeout(10)->get('https://usetrmnl.com/recipes.json', [
|
$response = Http::timeout(10)->get(
|
||||||
|
config('services.trmnl.base_url').'/recipes.json',
|
||||||
|
[
|
||||||
'sort-by' => 'newest',
|
'sort-by' => 'newest',
|
||||||
'page' => $this->page,
|
'page' => $this->page,
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
|
|
||||||
if (! $response->successful()) {
|
if (! $response->successful()) {
|
||||||
throw new RuntimeException('Failed to fetch TRMNL recipes');
|
throw new RuntimeException('Failed to fetch TRMNL recipes');
|
||||||
|
|
@ -86,11 +89,14 @@ class extends Component
|
||||||
try {
|
try {
|
||||||
$cacheKey = 'trmnl_recipes_search_'.md5($term).'_page_'.$this->page;
|
$cacheKey = 'trmnl_recipes_search_'.md5($term).'_page_'.$this->page;
|
||||||
$response = Cache::remember($cacheKey, 300, function () use ($term) {
|
$response = Cache::remember($cacheKey, 300, function () use ($term) {
|
||||||
$response = Http::get('https://usetrmnl.com/recipes.json', [
|
$response = Http::get(
|
||||||
|
config('services.trmnl.base_url').'/recipes.json',
|
||||||
|
[
|
||||||
'search' => $term,
|
'search' => $term,
|
||||||
'sort-by' => 'newest',
|
'sort-by' => 'newest',
|
||||||
'page' => $this->page,
|
'page' => $this->page,
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
|
|
||||||
if (! $response->successful()) {
|
if (! $response->successful()) {
|
||||||
throw new RuntimeException('Failed to search TRMNL recipes');
|
throw new RuntimeException('Failed to search TRMNL recipes');
|
||||||
|
|
@ -155,7 +161,7 @@ class extends Component
|
||||||
abort_unless(auth()->user() !== null, 403);
|
abort_unless(auth()->user() !== null, 403);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$zipUrl = "https://usetrmnl.com/api/plugin_settings/{$recipeId}/archive";
|
$zipUrl = config('services.trmnl.base_url')."/api/plugin_settings/{$recipeId}/archive";
|
||||||
|
|
||||||
$recipe = collect($this->recipes)->firstWhere('id', $recipeId);
|
$recipe = collect($this->recipes)->firstWhere('id', $recipeId);
|
||||||
|
|
||||||
|
|
@ -183,16 +189,21 @@ class extends Component
|
||||||
$this->previewData = [];
|
$this->previewData = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$response = Http::timeout(10)->get("https://usetrmnl.com/recipes/{$recipeId}.json");
|
$response = Http::timeout(10)->get(
|
||||||
|
config('services.trmnl.base_url')."/recipes/{$recipeId}.json"
|
||||||
|
);
|
||||||
|
|
||||||
if ($response->successful()) {
|
if ($response->successful()) {
|
||||||
$item = $response->json()['data'] ?? [];
|
$item = $response->json()['data'] ?? [];
|
||||||
$this->previewData = $this->mapRecipe($item);
|
$this->previewData = $this->mapRecipe($item);
|
||||||
} else {
|
} else {
|
||||||
// Fallback to searching for the specific recipe if single endpoint doesn't exist
|
// Fallback to searching for the specific recipe if single endpoint doesn't exist
|
||||||
$response = Http::timeout(10)->get('https://usetrmnl.com/recipes.json', [
|
$response = Http::timeout(10)->get(
|
||||||
|
config('services.trmnl.base_url').'/recipes.json',
|
||||||
|
[
|
||||||
'search' => $recipeId,
|
'search' => $recipeId,
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
|
|
||||||
if ($response->successful()) {
|
if ($response->successful()) {
|
||||||
$data = $response->json()['data'] ?? [];
|
$data = $response->json()['data'] ?? [];
|
||||||
|
|
@ -240,7 +251,9 @@ class extends Component
|
||||||
'installs' => data_get($item, 'stats.installs'),
|
'installs' => data_get($item, 'stats.installs'),
|
||||||
'forks' => data_get($item, 'stats.forks'),
|
'forks' => data_get($item, 'stats.forks'),
|
||||||
],
|
],
|
||||||
'detail_url' => isset($item['id']) ? ('https://usetrmnl.com/recipes/'.$item['id']) : null,
|
'detail_url' => isset($item['id'])
|
||||||
|
? config('services.trmnl.base_url').'/recipes/'.$item['id']
|
||||||
|
: null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}; ?>
|
}; ?>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ new class extends Component {}
|
||||||
<div class="mt-3 flex items-center justify-start gap-2">
|
<div class="mt-3 flex items-center justify-start gap-2">
|
||||||
<flux:input value="laravel-trmnl" readonly copyable class="max-w-42"/>
|
<flux:input value="laravel-trmnl" readonly copyable class="max-w-42"/>
|
||||||
<flux:button class="w-42"
|
<flux:button class="w-42"
|
||||||
href="https://usetrmnl.com/?ref=laravel-trmnl"
|
href="{{ config('services.trmnl.base_url') }}?ref=laravel-trmnl"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
icon:trailing="arrow-up-right">{{ __('Referral link') }}</flux:button>
|
icon:trailing="arrow-up-right">{{ __('Referral link') }}</flux:button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="grid" style="gap: 9px;">
|
<div class="grid" style="gap: 9px;">
|
||||||
<div class="row row--center col--span-3 col--end">
|
<div class="row row--center col--span-3 col--end">
|
||||||
<img class="weather-image" style="max-height: 150px; margin:auto;"
|
<img class="weather-image" style="max-height: 150px; margin:auto;"
|
||||||
src="https://usetrmnl.com/images/plugins/weather/wi-thermometer.svg">
|
src="{{ config('services.trmnl.base_url') }}/images/plugins/weather/wi-thermometer.svg">
|
||||||
</div>
|
</div>
|
||||||
<div class="col col--span-3 col--end">
|
<div class="col col--span-3 col--end">
|
||||||
<div class="item h--full">
|
<div class="item h--full">
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="meta"></div>
|
<div class="meta"></div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
{{-- <img class="weather-icon" src="https://usetrmnl.com/images/plugins/weather/wi-thermometer.svg"> --}}
|
{{-- <img class="weather-icon" src="{{ config('services.trmnl.base_url') }}/images/plugins/weather/wi-thermometer.svg"> --}}
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<span class="value value--small">{{ $weatherEntity['attributes']['wind_speed'] }} {{ $weatherEntity['attributes']['wind_speed_unit'] }}</span>
|
<span class="value value--small">{{ $weatherEntity['attributes']['wind_speed'] }} {{ $weatherEntity['attributes']['wind_speed_unit'] }}</span>
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="meta"></div>
|
<div class="meta"></div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
{{-- <img class="weather-icon" src="https://usetrmnl.com/images/weather/wi-raindrops.svg"> --}}
|
{{-- <img class="weather-icon" src="{{ config('services.trmnl.base_url') }}/images/weather/wi-raindrops.svg"> --}}
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<span class="value value--small">{{ $weatherEntity['attributes']['humidity'] }}%</span>
|
<span class="value value--small">{{ $weatherEntity['attributes']['humidity'] }}%</span>
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="meta"></div>
|
<div class="meta"></div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
{{-- <img class="weather-icon" src="https://usetrmnl.com/images/weather/wi-day-sunny.svg"> --}}
|
{{-- <img class="weather-icon" src="{{ config('services.trmnl.base_url') }}/images/weather/wi-day-sunny.svg"> --}}
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<span class="value value--xsmall">{{ Str::title($weatherEntity['state']) }}</span>
|
<span class="value value--xsmall">{{ Str::title($weatherEntity['state']) }}</span>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script src="https://usetrmnl.com/js/highcharts/12.3.0/highcharts.js"></script>
|
<script src="{{ config('services.trmnl.base_url') }}/js/highcharts/12.3.0/highcharts.js"></script>
|
||||||
<script src="https://usetrmnl.com/js/chartkick/5.0.1/chartkick.min.js"></script>
|
<script src="{{ config('services.trmnl.base_url') }}/js/chartkick/5.0.1/chartkick.min.js"></script>
|
||||||
|
|
||||||
<div class="view view--{{ size }}">
|
<div class="view view--{{ size }}">
|
||||||
<div class="layout layout--col gap--space-between">
|
<div class="layout layout--col gap--space-between">
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="grid" style="gap: 9px;">
|
<div class="grid" style="gap: 9px;">
|
||||||
<div class="row row--center col--span-3 col--end">
|
<div class="row row--center col--span-3 col--end">
|
||||||
<img class="weather-image" style="max-height: 150px; margin:auto;"
|
<img class="weather-image" style="max-height: 150px; margin:auto;"
|
||||||
src="https://usetrmnl.com/images/plugins/weather/wi-thermometer.svg">
|
src="{{ config('services.trmnl.base_url') }}/images/plugins/weather/wi-thermometer.svg">
|
||||||
</div>
|
</div>
|
||||||
<div class="col col--span-3 col--center">
|
<div class="col col--span-3 col--center">
|
||||||
<div class="item">
|
<div class="item">
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="meta"></div>
|
<div class="meta"></div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
{{-- <img class="weather-icon" src="https://usetrmnl.com/images/weather/wi-thermometer.svg">--}}
|
{{-- <img class="weather-icon" src="{{ config('services.trmnl.base_url') }}/images/weather/wi-thermometer.svg">--}}
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<span
|
<span
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="meta"></div>
|
<div class="meta"></div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
{{-- <img class="weather-icon" src="https://usetrmnl.com/images/weather/wi-raindrops.svg">--}}
|
{{-- <img class="weather-icon" src="{{ config('services.trmnl.base_url') }}/images/weather/wi-raindrops.svg">--}}
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<span class="value value--small">{{Arr::get($data, 'properties.timeseries.0.data.instant.details.relative_humidity', 'N/A')}}%</span>
|
<span class="value value--small">{{Arr::get($data, 'properties.timeseries.0.data.instant.details.relative_humidity', 'N/A')}}%</span>
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="meta"></div>
|
<div class="meta"></div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
{{-- <img class="weather-icon" src="https://usetrmnl.com/images/weather/wi-day-sunny.svg">--}}
|
{{-- <img class="weather-icon" src="{{ config('services.trmnl.base_url') }}/images/weather/wi-day-sunny.svg">--}}
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<span
|
<span
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@
|
||||||
href="{{ config('trmnl-blade.framework_css_url') }}">
|
href="{{ config('trmnl-blade.framework_css_url') }}">
|
||||||
@else
|
@else
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="https://usetrmnl.com/css/{{ config('trmnl-blade.framework_version', '1.2.0') }}/plugins.css">
|
href="{{ config('services.trmnl.base_url') }}/css/{{ config('trmnl-blade.framework_version', '1.2.0') }}/plugins.css">
|
||||||
@endif
|
@endif
|
||||||
@if (config('trmnl-blade.framework_js_url'))
|
@if (config('trmnl-blade.framework_js_url'))
|
||||||
<script src="{{ config('trmnl-blade.framework_js_url') }}"></script>
|
<script src="{{ config('trmnl-blade.framework_js_url') }}"></script>
|
||||||
@else
|
@else
|
||||||
<script src="https://usetrmnl.com/js/{{ config('trmnl-blade.framework_version', '1.2.0') }}/plugins.js"></script>
|
<script src="{{ config('services.trmnl.base_url') }}/js/{{ config('trmnl-blade.framework_version', '1.2.0') }}/plugins.js"></script>
|
||||||
@endif
|
@endif
|
||||||
<title>{{ $title ?? config('app.name') }}</title>
|
<title>{{ $title ?? config('app.name') }}</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@ test('firmware check command has correct signature', function (): void {
|
||||||
|
|
||||||
test('firmware check command runs without errors', function (): void {
|
test('firmware check command runs without errors', function (): void {
|
||||||
// Mock the firmware API response
|
// Mock the firmware API response
|
||||||
|
$baseUrl = config('services.trmnl.base_url');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'https://usetrmnl.com/api/firmware/latest' => Http::response([
|
$baseUrl.'/api/firmware/latest' => Http::response([
|
||||||
'version' => '1.0.0',
|
'version' => '1.0.0',
|
||||||
'url' => 'https://example.com/firmware.bin',
|
'url' => 'https://example.com/firmware.bin',
|
||||||
], 200),
|
], 200),
|
||||||
|
|
@ -33,8 +35,10 @@ test('firmware check command runs without errors', function (): void {
|
||||||
|
|
||||||
test('firmware check command runs with download flag', function (): void {
|
test('firmware check command runs with download flag', function (): void {
|
||||||
// Mock the firmware API response
|
// Mock the firmware API response
|
||||||
|
$baseUrl = config('services.trmnl.base_url');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'https://usetrmnl.com/api/firmware/latest' => Http::response([
|
$baseUrl.'/api/firmware/latest' => Http::response([
|
||||||
'version' => '1.0.0',
|
'version' => '1.0.0',
|
||||||
'url' => 'https://example.com/firmware.bin',
|
'url' => 'https://example.com/firmware.bin',
|
||||||
], 200),
|
], 200),
|
||||||
|
|
@ -57,8 +61,10 @@ test('firmware check command runs with download flag', function (): void {
|
||||||
|
|
||||||
test('firmware check command can run successfully', function (): void {
|
test('firmware check command can run successfully', function (): void {
|
||||||
// Mock the firmware API response
|
// Mock the firmware API response
|
||||||
|
$baseUrl = config('services.trmnl.base_url');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'https://usetrmnl.com/api/firmware/latest' => Http::response([
|
$baseUrl.'/api/firmware/latest' => Http::response([
|
||||||
'version' => '1.0.0',
|
'version' => '1.0.0',
|
||||||
'url' => 'https://example.com/firmware.bin',
|
'url' => 'https://example.com/firmware.bin',
|
||||||
], 200),
|
], 200),
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ test('fetch device models job can be dispatched', function (): void {
|
||||||
test('fetch device models job handles successful api response', function (): void {
|
test('fetch device models job handles successful api response', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => Http::response([
|
config('services.trmnl.base_url').'/api/models' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'name' => 'test-model',
|
'name' => 'test-model',
|
||||||
|
|
@ -82,7 +82,7 @@ test('fetch device models job handles successful api response', function (): voi
|
||||||
test('fetch device models job handles multiple device models', function (): void {
|
test('fetch device models job handles multiple device models', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => Http::response([
|
config('services.trmnl.base_url').'/api/models' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'name' => 'model-1',
|
'name' => 'model-1',
|
||||||
|
|
@ -136,7 +136,7 @@ test('fetch device models job handles multiple device models', function (): void
|
||||||
test('fetch device models job handles empty data array', function (): void {
|
test('fetch device models job handles empty data array', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => Http::response([
|
config('services.trmnl.base_url').'/api/models' => Http::response([
|
||||||
'data' => [],
|
'data' => [],
|
||||||
], 200),
|
], 200),
|
||||||
]);
|
]);
|
||||||
|
|
@ -158,7 +158,7 @@ test('fetch device models job handles empty data array', function (): void {
|
||||||
test('fetch device models job handles missing data field', function (): void {
|
test('fetch device models job handles missing data field', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => Http::response([
|
config('services.trmnl.base_url').'/api/models' => Http::response([
|
||||||
'message' => 'No data available',
|
'message' => 'No data available',
|
||||||
], 200),
|
], 200),
|
||||||
]);
|
]);
|
||||||
|
|
@ -180,7 +180,7 @@ test('fetch device models job handles missing data field', function (): void {
|
||||||
test('fetch device models job handles non-array data', function (): void {
|
test('fetch device models job handles non-array data', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => Http::response([
|
config('services.trmnl.base_url').'/api/models' => Http::response([
|
||||||
'data' => 'invalid-data',
|
'data' => 'invalid-data',
|
||||||
], 200),
|
], 200),
|
||||||
]);
|
]);
|
||||||
|
|
@ -202,7 +202,7 @@ test('fetch device models job handles non-array data', function (): void {
|
||||||
test('fetch device models job handles api failure', function (): void {
|
test('fetch device models job handles api failure', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => Http::response([
|
config('services.trmnl.base_url').'/api/models' => Http::response([
|
||||||
'error' => 'Internal Server Error',
|
'error' => 'Internal Server Error',
|
||||||
], 500),
|
], 500),
|
||||||
]);
|
]);
|
||||||
|
|
@ -227,7 +227,7 @@ test('fetch device models job handles api failure', function (): void {
|
||||||
test('fetch device models job handles network exception', function (): void {
|
test('fetch device models job handles network exception', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => function (): void {
|
config('services.trmnl.base_url').'/api/models' => function (): void {
|
||||||
throw new Exception('Network connection failed');
|
throw new Exception('Network connection failed');
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
@ -249,7 +249,7 @@ test('fetch device models job handles network exception', function (): void {
|
||||||
test('fetch device models job handles device model with missing name', function (): void {
|
test('fetch device models job handles device model with missing name', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => Http::response([
|
config('services.trmnl.base_url').'/api/models' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'label' => 'Model without name',
|
'label' => 'Model without name',
|
||||||
|
|
@ -280,7 +280,7 @@ test('fetch device models job handles device model with missing name', function
|
||||||
test('fetch device models job handles device model with partial data', function (): void {
|
test('fetch device models job handles device model with partial data', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => Http::response([
|
config('services.trmnl.base_url').'/api/models' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'name' => 'minimal-model',
|
'name' => 'minimal-model',
|
||||||
|
|
@ -329,7 +329,7 @@ test('fetch device models job updates existing device model', function (): void
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => Http::response([
|
config('services.trmnl.base_url').'/api/models' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'name' => 'existing-model',
|
'name' => 'existing-model',
|
||||||
|
|
@ -372,7 +372,7 @@ test('fetch device models job updates existing device model', function (): void
|
||||||
test('fetch device models job handles processing exception for individual model', function (): void {
|
test('fetch device models job handles processing exception for individual model', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
'usetrmnl.com/api/palettes' => Http::response(['data' => []], 200),
|
||||||
'usetrmnl.com/api/models' => Http::response([
|
config('services.trmnl.base_url').'/api/models' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'name' => 'valid-model',
|
'name' => 'valid-model',
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,10 @@ beforeEach(function (): void {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it creates new firmware record when polling', function (): void {
|
test('it creates new firmware record when polling', function (): void {
|
||||||
|
$baseUrl = config('services.trmnl.base_url');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'https://usetrmnl.com/api/firmware/latest' => Http::response([
|
$baseUrl.'/api/firmware/latest' => Http::response([
|
||||||
'version' => '1.0.0',
|
'version' => '1.0.0',
|
||||||
'url' => 'https://example.com/firmware.bin',
|
'url' => 'https://example.com/firmware.bin',
|
||||||
], 200),
|
], 200),
|
||||||
|
|
@ -32,8 +34,10 @@ test('it updates existing firmware record when polling', function (): void {
|
||||||
'latest' => true,
|
'latest' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$baseUrl = config('services.trmnl.base_url');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'https://usetrmnl.com/api/firmware/latest' => Http::response([
|
$baseUrl.'/api/firmware/latest' => Http::response([
|
||||||
'version' => '1.0.0',
|
'version' => '1.0.0',
|
||||||
'url' => 'https://new-url.com/firmware.bin',
|
'url' => 'https://new-url.com/firmware.bin',
|
||||||
], 200),
|
], 200),
|
||||||
|
|
@ -52,8 +56,10 @@ test('it marks previous firmware as not latest when new version is found', funct
|
||||||
'latest' => true,
|
'latest' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$baseUrl = config('services.trmnl.base_url');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'https://usetrmnl.com/api/firmware/latest' => Http::response([
|
$baseUrl.'/api/firmware/latest' => Http::response([
|
||||||
'version' => '1.1.0',
|
'version' => '1.1.0',
|
||||||
'url' => 'https://example.com/firmware.bin',
|
'url' => 'https://example.com/firmware.bin',
|
||||||
], 200),
|
], 200),
|
||||||
|
|
@ -66,8 +72,10 @@ test('it marks previous firmware as not latest when new version is found', funct
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles connection exception gracefully', function (): void {
|
test('it handles connection exception gracefully', function (): void {
|
||||||
|
$baseUrl = config('services.trmnl.base_url');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'https://usetrmnl.com/api/firmware/latest' => function (): void {
|
$baseUrl.'/api/firmware/latest' => function (): void {
|
||||||
throw new ConnectionException('Connection failed');
|
throw new ConnectionException('Connection failed');
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
@ -79,8 +87,10 @@ test('it handles connection exception gracefully', function (): void {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles invalid response gracefully', function (): void {
|
test('it handles invalid response gracefully', function (): void {
|
||||||
|
$baseUrl = config('services.trmnl.base_url');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'https://usetrmnl.com/api/firmware/latest' => Http::response(null, 200),
|
$baseUrl.'/api/firmware/latest' => Http::response(null, 200),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
(new FirmwarePollJob)->handle();
|
(new FirmwarePollJob)->handle();
|
||||||
|
|
@ -90,8 +100,10 @@ test('it handles invalid response gracefully', function (): void {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles missing version in response gracefully', function (): void {
|
test('it handles missing version in response gracefully', function (): void {
|
||||||
|
$baseUrl = config('services.trmnl.base_url');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'https://usetrmnl.com/api/firmware/latest' => Http::response([
|
$baseUrl.'/api/firmware/latest' => Http::response([
|
||||||
'url' => 'https://example.com/firmware.bin',
|
'url' => 'https://example.com/firmware.bin',
|
||||||
], 200),
|
], 200),
|
||||||
]);
|
]);
|
||||||
|
|
@ -103,8 +115,10 @@ test('it handles missing version in response gracefully', function (): void {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles missing url in response gracefully', function (): void {
|
test('it handles missing url in response gracefully', function (): void {
|
||||||
|
$baseUrl = config('services.trmnl.base_url');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'https://usetrmnl.com/api/firmware/latest' => Http::response([
|
$baseUrl.'/api/firmware/latest' => Http::response([
|
||||||
'version' => '1.0.0',
|
'version' => '1.0.0',
|
||||||
], 200),
|
], 200),
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use Livewire\Livewire;
|
||||||
|
|
||||||
it('loads newest TRMNL recipes on mount', function (): void {
|
it('loads newest TRMNL recipes on mount', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/recipes.json*' => Http::response([
|
config('services.trmnl.base_url').'/recipes.json*' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'id' => 123,
|
'id' => 123,
|
||||||
|
|
@ -33,7 +33,7 @@ it('loads newest TRMNL recipes on mount', function (): void {
|
||||||
|
|
||||||
it('shows preview button when screenshot_url is provided', function (): void {
|
it('shows preview button when screenshot_url is provided', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/recipes.json*' => Http::response([
|
config('services.trmnl.base_url').'/recipes.json*' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'id' => 123,
|
'id' => 123,
|
||||||
|
|
@ -57,7 +57,7 @@ it('shows preview button when screenshot_url is provided', function (): void {
|
||||||
it('searches TRMNL recipes when search term is provided', function (): void {
|
it('searches TRMNL recipes when search term is provided', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
// First call (mount -> newest)
|
// First call (mount -> newest)
|
||||||
'usetrmnl.com/recipes.json?*' => Http::sequence()
|
config('services.trmnl.base_url').'/recipes.json?*' => Http::sequence()
|
||||||
->push([
|
->push([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
|
|
@ -98,7 +98,7 @@ it('installs plugin successfully when user is authenticated', function (): void
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/recipes.json*' => Http::response([
|
config('services.trmnl.base_url').'/recipes.json*' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'id' => 123,
|
'id' => 123,
|
||||||
|
|
@ -110,7 +110,7 @@ it('installs plugin successfully when user is authenticated', function (): void
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
], 200),
|
], 200),
|
||||||
'usetrmnl.com/api/plugin_settings/123/archive*' => Http::response('fake zip content', 200),
|
config('services.trmnl.base_url').'/api/plugin_settings/123/archive*' => Http::response('fake zip content', 200),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
@ -125,7 +125,7 @@ it('installs plugin successfully when user is authenticated', function (): void
|
||||||
|
|
||||||
it('shows error when user is not authenticated', function (): void {
|
it('shows error when user is not authenticated', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/recipes.json*' => Http::response([
|
config('services.trmnl.base_url').'/recipes.json*' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'id' => 123,
|
'id' => 123,
|
||||||
|
|
@ -151,7 +151,7 @@ it('shows error when plugin installation fails', function (): void {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/recipes.json*' => Http::response([
|
config('services.trmnl.base_url').'/recipes.json*' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'id' => 123,
|
'id' => 123,
|
||||||
|
|
@ -163,7 +163,7 @@ it('shows error when plugin installation fails', function (): void {
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
], 200),
|
], 200),
|
||||||
'usetrmnl.com/api/plugin_settings/123/archive*' => Http::response('invalid zip content', 200),
|
config('services.trmnl.base_url').'/api/plugin_settings/123/archive*' => Http::response('invalid zip content', 200),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
@ -178,7 +178,7 @@ it('shows error when plugin installation fails', function (): void {
|
||||||
|
|
||||||
it('previews a recipe with async fetch', function (): void {
|
it('previews a recipe with async fetch', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/recipes.json*' => Http::response([
|
config('services.trmnl.base_url').'/recipes.json*' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'id' => 123,
|
'id' => 123,
|
||||||
|
|
@ -190,7 +190,7 @@ it('previews a recipe with async fetch', function (): void {
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
], 200),
|
], 200),
|
||||||
'usetrmnl.com/recipes/123.json' => Http::response([
|
config('services.trmnl.base_url').'/recipes/123.json' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
'id' => 123,
|
'id' => 123,
|
||||||
'name' => 'Weather Chum Updated',
|
'name' => 'Weather Chum Updated',
|
||||||
|
|
@ -216,7 +216,7 @@ it('previews a recipe with async fetch', function (): void {
|
||||||
|
|
||||||
it('supports pagination and loading more recipes', function (): void {
|
it('supports pagination and loading more recipes', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/recipes.json?sort-by=newest&page=1' => Http::response([
|
config('services.trmnl.base_url').'/recipes.json?sort-by=newest&page=1' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'id' => 1,
|
'id' => 1,
|
||||||
|
|
@ -229,7 +229,7 @@ it('supports pagination and loading more recipes', function (): void {
|
||||||
],
|
],
|
||||||
'next_page_url' => '/recipes.json?page=2',
|
'next_page_url' => '/recipes.json?page=2',
|
||||||
], 200),
|
], 200),
|
||||||
'usetrmnl.com/recipes.json?sort-by=newest&page=2' => Http::response([
|
config('services.trmnl.base_url').'/recipes.json?sort-by=newest&page=2' => Http::response([
|
||||||
'data' => [
|
'data' => [
|
||||||
[
|
[
|
||||||
'id' => 2,
|
'id' => 2,
|
||||||
|
|
@ -258,7 +258,7 @@ it('supports pagination and loading more recipes', function (): void {
|
||||||
|
|
||||||
it('resets pagination when search term changes', function (): void {
|
it('resets pagination when search term changes', function (): void {
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'usetrmnl.com/recipes.json?sort-by=newest&page=1' => Http::sequence()
|
config('services.trmnl.base_url').'/recipes.json?sort-by=newest&page=1' => Http::sequence()
|
||||||
->push([
|
->push([
|
||||||
'data' => [['id' => 1, 'name' => 'Initial 1']],
|
'data' => [['id' => 1, 'name' => 'Initial 1']],
|
||||||
'next_page_url' => '/recipes.json?page=2',
|
'next_page_url' => '/recipes.json?page=2',
|
||||||
|
|
@ -267,7 +267,7 @@ it('resets pagination when search term changes', function (): void {
|
||||||
'data' => [['id' => 3, 'name' => 'Initial 1 Again']],
|
'data' => [['id' => 3, 'name' => 'Initial 1 Again']],
|
||||||
'next_page_url' => null,
|
'next_page_url' => null,
|
||||||
]),
|
]),
|
||||||
'usetrmnl.com/recipes.json?search=weather&sort-by=newest&page=1' => Http::response([
|
config('services.trmnl.base_url').'/recipes.json?search=weather&sort-by=newest&page=1' => Http::response([
|
||||||
'data' => [['id' => 2, 'name' => 'Weather Result']],
|
'data' => [['id' => 2, 'name' => 'Weather Result']],
|
||||||
'next_page_url' => null,
|
'next_page_url' => null,
|
||||||
]),
|
]),
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ test('hasMissingRequiredConfigurationFields returns true when required xhrSelect
|
||||||
'field_type' => 'xhrSelect',
|
'field_type' => 'xhrSelect',
|
||||||
'name' => 'Baseball Team',
|
'name' => 'Baseball Team',
|
||||||
'description' => 'Select your team',
|
'description' => 'Select your team',
|
||||||
'endpoint' => 'https://usetrmnl.com/custom_plugin_example_xhr_select.json',
|
'endpoint' => config('services.trmnl.base_url').'/custom_plugin_example_xhr_select.json',
|
||||||
// Not marked as optional, so it's required
|
// Not marked as optional, so it's required
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
@ -252,7 +252,7 @@ test('hasMissingRequiredConfigurationFields returns false when required xhrSelec
|
||||||
'field_type' => 'xhrSelect',
|
'field_type' => 'xhrSelect',
|
||||||
'name' => 'Baseball Team',
|
'name' => 'Baseball Team',
|
||||||
'description' => 'Select your team',
|
'description' => 'Select your team',
|
||||||
'endpoint' => 'https://usetrmnl.com/custom_plugin_example_xhr_select.json',
|
'endpoint' => config('services.trmnl.base_url').'/custom_plugin_example_xhr_select.json',
|
||||||
// Not marked as optional, so it's required
|
// Not marked as optional, so it's required
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue