DIY server in PHP/Laravel for self hosted screen generation.
Find a file
dependabot[bot] 9f12161dc9
chore(deps): bump axios from 1.8.1 to 1.8.2
Bumps [axios](https://github.com/axios/axios) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 12:44:24 +00:00
.devcontainer feat: add .devcontainer support 2025-03-06 22:13:28 +01:00
.github tests calculate code coverage 2025-03-08 19:54:25 +01:00
app add features 2025-03-03 21:24:22 +01:00
bootstrap add features 2025-03-03 21:24:22 +01:00
config feat: option to refresh proxy by cron syntax, update readme 2025-03-08 19:52:42 +01:00
database fix: compatibility with trmnl simulator 2025-03-04 19:06:58 +01:00
docker add features 2025-03-03 21:24:22 +01:00
public feat: update to Laravel 12 starter kit (#1) 2025-02-25 12:15:35 +01:00
resources fix: home assistant markup example 2025-03-10 13:32:25 +01:00
routes fix: log 2025-03-10 13:32:25 +01:00
screenshots feat: add tests, chore: update readme 2025-03-04 16:45:30 +01:00
storage initial commit 2025-02-12 22:15:57 +01:00
tests fix: test, docker env deprecation warning 2025-03-10 13:32:25 +01:00
.dockerignore add features 2025-03-03 21:24:22 +01:00
.editorconfig Set up a fresh Laravel app 2025-02-08 15:05:41 +01:00
.env.example add features 2025-03-03 21:24:22 +01:00
.gitattributes Set up a fresh Laravel app 2025-02-08 15:05:41 +01:00
.gitignore feat: update to Laravel 12 starter kit (#1) 2025-02-25 12:15:35 +01:00
artisan Set up a fresh Laravel app 2025-02-08 15:05:41 +01:00
composer.json chore: update dependencies 2025-03-06 13:04:52 +01:00
composer.lock chore: update dependencies 2025-03-10 13:32:25 +01:00
docker-compose.yml add features 2025-03-03 21:24:22 +01:00
Dockerfile fix: test, docker env deprecation warning 2025-03-10 13:32:25 +01:00
example.png initial commit 2025-02-12 22:15:57 +01:00
LICENSE.md Create LICENSE.md 2025-02-12 22:35:04 +01:00
package-lock.json chore(deps): bump axios from 1.8.1 to 1.8.2 2025-03-10 12:44:24 +00:00
package.json chore(deps): bump axios from 1.8.1 to 1.8.2 2025-03-10 12:44:24 +00:00
phpunit.xml feat: update to Laravel 12 starter kit (#1) 2025-02-25 12:15:35 +01:00
README.md feat: option to refresh proxy by cron syntax, update readme 2025-03-08 19:52:42 +01:00
README_byos-screenshot.png feat: add tests, chore: update readme 2025-03-04 16:45:30 +01:00
vite.config.js feat: update to Laravel 12 starter kit (#1) 2025-02-25 12:15:35 +01:00

TRMNL BYOS (PHP/Laravel)

tests

Laravel Trmnl Server is a self-hostable implementation of a TRMNL server, built with Laravel. It enables you to manage TRMNL devices, generate screens dynamically, and can act as a proxy for the TRMNL API (native plugin system). Inspired by usetrmnl/byos_sinatra.

If you are looking for a Laravel package designed to streamline the development of both public and private TRMNL plugins, check out bnussbau/trmnl-laravel.

Screenshot

Key Features

  • 📡 Device Information Display battery status, WiFi strength, firmware version, and more.
  • 🔍 Auto-Join Automatically detects and adds devices from your local network.
  • 🖥️ Screen Generation Supports Markup, API, or update via Code.
  • 🔄 TRMNL API Proxy Can act as a proxy for the TRMNL Display API (requires TRMNL Developer Edition).
    • This enables a hybrid setup for example, you can update your custom Train Monitor every 5 minutes in the morning, while displaying native TRMNL plugins throughout the day.
  • 🐳 Deployment Dockerized setup for easier hosting (Dockerfile, docker-compose).
  • 🛠️ Devcontainer support for easier development.

🎯 Target Audience

This project is for developers who are looking for a self-hosted server for devices running the TRMNL firmware. It serves as a starter kit, giving you the flexibility to build and extend it however you like.

Support ❤️

This repo is maintained voluntarily by @bnussbau.

Support the development of this package by purchasing a TRMNL device through our referral link: https://usetrmnl.com/?ref=laravel-trmnl. At checkout, use the code laravel-trmnl to receive a $15 discount on your purchase.

Requirements

Installation

Clone the repository

git clone git@github.com:bnussbau/laravel-trmnl-server.git

Copy environment file

cp .env.example .env
php artisan key:generate

Install dependencies

composer install
npm i

Run migrations

php artisan migrate --seed

Run the server

To make your server accessible in the network, you can run the following command:

php artisan serve  --host=0.0.0.0 --port 4567

Docker

Use the provided Dockerfile, or docker-compose file to run the server in a container. You can persist the database file by mounting a volume to /var/www/html/database/database.sqlite.

# docker-compose.yaml
volumes:
    - ./database/database.sqlite:/var/www/html/database/database.sqlite

Usage

Environment Variables

environment description default
TRMNL_PROXY_BASE_URL Base URL of the native TRMNL service https://trmnl.app
TRMNL_PROXY_REFRESH_MINUTES How often should the server fetch new images from native service 15
REGISTRATION_ENABLED Allow user registration via Webinterface 1
FORCE_HTTPS If your server handles SSL termination, enforce HTTPS. 0

Login

If your environment is local, you can access the server at http://localhost:4567 and login with user / password admin@example.com / admin@example.com, otherwise register. With environment variable REGISTRATION_ENABLED you can control, if registration is allowed.

Add Your TRMNL Device

Auto-Join (Local Network)
  1. Switch on the “Permit Auto-Join” toggle in the header. For that to work only one user can be registered.
  2. New devices on your local network will be detected and added automatically when connecting to the server.

This is the easiest way to connect your devices with minimal effort.

Manually
  1. Open the Devices page: 👉 http://localhost:4567/devices
  2. Click “Add New Device”.
  3. Retrieve your TRMNL MAC Address and API Key:
  • You can grab the TRMNL Mac Address and API Key from the TRMNL Dashboard
  • Alternatively, debug incoming requests to /api/setup to determine them

⚙️ Configure Server for Device

📌 Firmware Version 1.4.6 or Newer

  • Setup device
  • After entering Wifi credentials, choose "Custom Server"
  • Point to the URL of your server

Firmware Older Than 1.4.6

If your device firmware is older than 1.4.6, you need to flash a new firmware version to point it to your server.

See this YouTube guide: https://www.youtube.com/watch?v=3xehPW-PCOM

🖥️ Generate Screens

🎨 Blade View

php artisan trmnl:screen:generate

Generate via API

You can dynamically update screens by sending a POST request.

  • Send a POST request to /api/screen with the following payload
Header

Authorization Bearer <TOKEN>

Body
{
    "markup": "<h1>Hello World</h1>"
}

Token can be retrieved under Plugins > API in the Web Interface.

Markup via Web Interface

  1. Navigate to Plugins > Markup in the Web Interface.
  2. Enter your markup manually or select from the available templates.
  3. Save and apply the changes.

🛠️ Generate Screens Programmatically

You can fetch external data, process it, and generate screens dynamically.

  • Fetch data from an external source.
  • Either render it in a Blade view or directly insert markup.
  • Use Laravels scheduler to automate updates.

📌 Example: Fetch Train Monitor Data

This example retrieves data from trmnl-train-monitor and updates the screen periodically.

Step 1: Create a new Artisan Command
php artisan make:command PluginTrainMonitorFetch
Step 2: Edit PluginTrainMonitorFetch.php
class PluginTrainMonitorFetch extends Command
{
    protected $signature = 'plugin:train:fetch';

    protected $description = 'Fetches train monitor data and updates the screen';

    public function handle(): void
    {
        $markup = Http::get('https://oebb.trmnl.yourserver.at/markup')->json('markup');
        GenerateScreenJob::dispatchSync(1, $markup);
    }
}
Step 3: Schedule the Command in console.php
Schedule::command('plugin:train:fetch')
    ->everyFiveMinutes()
    ->timezone('Europe/Vienna')
    ->between('5:00', '18:00');

This will automatically update the screen every 5 minutes between 5:00 AM and 6:00 PM local time.

🏗️ Roadmap

Here are some features and improvements that are open for contribution:

🔌 Plugin System
  • Enable configurable plugins via the Web Interface.
  • Ensure compatibility with the trmnl-laravel package.
  • Implement auto-discovery for plugins.
Scheduling
  • Move task scheduling from console.php to a Web Interface.
  • Allow users to configure custom schedule intervals.
🖥️ “Native” Plugins
  • Add built-in plugins such as (as an example):
    • ☁️ Weather
    • 💬 Quotes
    • 🏡 Home Assistant integration
  • Provide Web UI controls to enable/disable plugins.
📦 Visual Studio Code Devcontainer
  • Add a .devcontainer to this repo for easier development with Docker.
Improve Code Coverage
  • Expand Pest tests to cover more functionality.
  • Increase code coverage (currently at 86.9%).

🤝 Contribution

Contributions are welcome! If youd like to improve the project, follow these steps:

  1. Open an Issue
    • Before submitting a pull request, create an issue to discuss your idea.
    • Clearly describe the feature or bug fix you want to work on.
  2. Fork the Repository & Create a Branch
  3. Make Your Changes & Add Tests
    • Ensure your code follows best practices.
    • Add Pest tests to cover your changes.
  4. Run Tests
    • php artisan test
  5. Submit a Pull Request (PR)
    • Push your branch and create a PR.
    • Provide a clear description of your changes.

Thank you for contributing!

License

MIT