LaraPaper Documentation

repository·main·Indexed 18 days ago

https://github.com/usetrmnl/larapaper

A self-hostable TRMNL server implementation built with Laravel. LaraPaper allows users to host their own TRMNL device management system, generate custom screens via plugins, Blade templates, or API, and optionally proxy native TRMNL cloud services. It includes features for Docker deployment, OTA firmware updates, and device management via a web interface or CLI.

Tokens
11.8K
Snippets
35
Records
64
Agent score
62%

What's inside LaraPaper

  1. Overview of LaraPaper

    main

    LaraPaper is a self-hostable implementation of a TRMNL server (BYOS - Bring Your Own Server) built with Laravel. It allows you to manage TRMNL devices and generate screens using several methods:

    • Native Plugins: Includes support for mashups.
    • Recipes: Import from the Community Catalog or the official TRMNL Catalog.
    • API: Custom screen generation via the API.
    • Markup/Code: Direct updates via markup or code.

    LaraPaper can also act as a proxy for the native TRMNL cloud service in hybrid local + cloud setups (Developer Edition).

  2. How native plugins work in LaraPaper

    main

    Native plugins are specialized handlers that complement standard recipe plugins (which use Liquid/Blade). While recipes are user-managed markup, native plugins are defined in code via a PluginHandler and can handle complex logic like webhooks, automated image generation, or external URL screenshots.

    Core Abstractions

    • PluginHandler: The abstract contract defining metadata, webhook handling, and the display pipeline logic.
    • PluginRegistry: A singleton that maps unique keys to handler instances.
    • Plugin model: Stores the plugin_type (the handler's key). Use $plugin->handler() to resolve the logic.
    • PluginOutput: An enum that tells the GenerateScreenJob how to process the plugin's assets (e.g., whether to render HTML or use a pre-processed image).
    • PluginActionController: The single HTTP entrypoint that routes incoming webhooks to the correct handler based on the plugin's type.
    // Resolving a handler from a plugin instance
    $handler = $plugin->handler();
    
    if ($handler) {
        // Perform handler-specific logic
    }
  3. Configure the display pipeline using `PluginOutput`

    main

    The GenerateScreenJob determines how to render a plugin's content based on the value returned by $plugin->handler()->output(). Use these values to control the e-paper pipeline:

    • PluginOutput::ProcessedImage: Use this if your plugin (like image_webhook) already stores a device-ready file at images/generated/{uuid}.{ext}. The job will skip the Browser/Image stages and copy the file directly to the device.
    • PluginOutput::Image: Use this for plugins that require a browser stage but provide a specific source (like screenshot plugins). You must also override configureBrowserStage() in your handler to bind the stage (e.g., using BrowserStage::url() instead of html()).
    • PluginOutput::Html (Default): The plugin's markup is passed through the full BrowserStage + ImageStage pipeline via ImageGenerationService.
  4. How LaraPaper works

    main

    LaraPaper acts as a 'Bring Your Own Server' (BYOS) for TRMNL devices. The core workflow follows these steps:

    1. Request: A TRMNL device calls the /api/display endpoint.
    2. Rendering: LaraPaper uses Puppeteer to render the selected screen content.
    3. Image Processing: The image pipeline uses ImageMagick to convert the rendered content into a PNG variant specifically matched to the active device model.
    4. Delivery: The device downloads and displays the resulting image.

    This architecture allows for more frequent updates (e.g., every 5 minutes) compared to the native TRMNL cloud flow, which is typically designed around a 15-minute refresh interval.

  5. How LaraPaper renders screens

    main

    LaraPaper follows a specific rendering pipeline to deliver content to hardware devices:

    1. Request: A device calls the /api/display endpoint.
    2. Rendering: LaraPaper renders the selected screen using Puppeteer.
    3. Conversion: The rendered output is converted into a device-specific PNG using ImageMagick.
    4. Delivery: The device downloads and displays the resulting image.
  6. Access LaraPaper for the first time

    main

    Depending on your environment, use the following credentials or methods to log in:

    • Local Development: Access via http://localhost:4567. If the database has been seeded, use admin@example.com / admin@example.com.
    • Production: Register a new user via the UI. To prevent others from registering, set REGISTRATION_ENABLED=0 in your environment variables.
  7. Deploy LaraPaper via Docker Compose

    main

    LaraPaper can be deployed using Docker. The production Docker Compose file is located at docker/prod/docker-compose.yml.

    Updating via Docker Compose

    To update your installation, run:

    docker compose pull
    docker compose down
    docker compose up -d

    Backing up the Database

    To back up your SQLite database, find the container ID and copy the file:

    docker ps # find container id
    docker cp {{CONTAINER_ID}}:/var/www/html/database/storage/database.sqlite database_backup.sqlite
  8. Add a TRMNL Device

    main

    Auto-Join (Local Network)

    1. Switch on the Permit Auto-Join toggle in the LaraPaper header.
    2. New devices on your local network will be detected and added automatically when they connect to the server.

    Manual Addition

    1. Navigate to the Devices page: http://localhost:4567/devices.
    2. Click Add New Device.
    3. Provide the TRMNL MAC Address and API Key. You can find these in the TRMNL Dashboard or by debugging requests to /api/setup.
  9. Connect devices via Auto-join (local network)

    main

    If your TRMNL device and LaraPaper server are on the same local network, you can use Auto-join for minimal configuration.

    1. Enable Permit Auto-Join in the LaraPaper header.
    2. Note that only one registered user is supported for this feature.
    3. Once enabled, devices on your local network will be automatically detected and added when they connect.