### settings.yml example Source: https://help.trmnl.com/en/articles/10542599-importing-and-exporting-private-plugins Example configuration for a private plugin. Includes required fields (name, strategy, refresh_interval) and optional fields for polling, static data, and custom form fields. ```yaml --- # required: name: My Awesome Plugin strategy: polling # polling | webhook | static refresh_interval: 60 # minutes; 15 | 60 | 360 | 720 | 1440 # polling strategy only: polling_url: https://example.com polling_headers: '' polling_verb: GET # GET | POST # static strategy only: static_data: '{"foo": "bar"}' # JSON string # generic options: no_screen_padding: 'no' # 'no' | 'yes' (quotes are necessary!) dark_mode: 'no' # 'no' | 'yes' (quotes are necessary!) # custom plugin form fields (optional, see docs): custom_fields: - keyname: units name: Units field_type: select options: - Metric - Imperial ``` -------------------------------- ### Output of upcase filter example Source: https://help.trmnl.com/en/articles/10671186-liquid-101 Shows the rendered output of the upcase filter example. ```Text Task "WALK THE DOG" is due on 2025-02-24 ``` -------------------------------- ### Example generated URLs from the Liquid template Source: https://help.trmnl.com/en/articles/12689499-dynamic-polling-urls Example output showing the URLs generated by the Liquid template when the user provides recipe IDs 49610, 18267, and 11914. ```text https://trmnl.com/recipes/49610.json https://trmnl.com/recipes/18267.json https://trmnl.com/recipes/11914.json ``` -------------------------------- ### Output of chained filter percentage example Source: https://help.trmnl.com/en/articles/10671186-liquid-101 Shows the rendered output of the chained filter percentage example. ```Text 20 is 40% of 50 ``` -------------------------------- ### Example Database Configuration for Support Tickets Source: https://help.trmnl.com/en/articles/11882090-notion An example configuration for a Notion database plugin showing a support ticket display with title field, status field, labeled properties, listed properties, sort settings, and a placeholder for the filter object. ```text Database: Customer Support Tickets Title Field: Ticket Title Status Field: Priority Labeled Properties: Status, Assignee, Customer Tier Listed Properties: Description, Last Updated, Response Time Sort By: created_time Sort Direction: Descending Filter: (see filter example below) ``` -------------------------------- ### Install TRMNL CLI with Homebrew Source: https://help.trmnl.com/en/articles/17226209-trmnl-cli Installs the TRMNL CLI using Homebrew on macOS or Linux. Requires Homebrew to be installed. ```bash brew install usetrmnl/tap/trmnl ``` -------------------------------- ### Accessing data from plugin_instance_select Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder Example of accessing data from the selected plugin instance using the field keyname. Shows how to reference temperature and humidity values. ```text Temperature: {{ weather_source.temperature }}° Humidity: {{ weather_source.humitidy }} ``` -------------------------------- ### PHP Serverless run function using curl Source: https://help.trmnl.com/en/articles/14130649-serverless PHP example using the standard curl extension. The run function performs a GET requestache and returns an associative array with a quote. ```php function run($input) { $ch = curl_init("https://dummyjson.com/quotes?limit=1"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $data = json_decode(curl_exec($ch), true); unset($ch); return ["quote" => $data["quotes"][0]]; } ``` -------------------------------- ### Example responses from xhrSelectSearch test endpoint Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder Example JSON responses from the test endpoint for different query values. The first response is for query=null, the second for query=goa. These show the expected format for filtering results. ```json # https://trmnl.com/custom_plugin_example_xhr_select_search.json # query=null [ { 'id' => 'db-123', 'name' => 'Project Tasks' }, { 'id' => 'db-456', 'name' => 'Team Goals' }, { 'id' => 'db-789', 'name' => 'Family Goals' } ] # query=goa [ { 'id' => 'db-456', 'name' => 'Team Goals' }, { 'id' => 'db-789', 'name' => 'Family Goals' } ] ``` -------------------------------- ### Liquid for loop output example Source: https://help.trmnl.com/en/articles/10671186-liquid-101 Shows the rendered output of the for loop, displaying both tasks from the JSON data. ```text Task "Walk the dog" is due on 2025-02-24 Task "Half-Life 3 Release" is due on 2033-10-08 ``` -------------------------------- ### Install TRMNL agent skills in Gemini CLI Source: https://help.trmnl.com/en/articles/17432548-mcp-server Run this command to install the TRMNL agent skills extension, which adds the MCP server and the TRMNL agent skill to Gemini CLI. ```bash gemini extensions install https://github.com/usetrmnl/trmnl-agent-skills ``` -------------------------------- ### Mixing Liquid and JavaScript - basic example Source: https://help.trmnl.com/en/articles/10693981-advanced-liquid Shows how to embed Liquid variables inside JavaScript code. Useful for passing data from Liquid to JS, e.g., for building charts. ```html ``` -------------------------------- ### Call TRMNL API with bearer token - GET /api/devices Source: https://help.trmnl.com/en/articles/17225165-developer-apps Call the TRMNL API by sending the access token as a bearer token. This example fetches devices and shows a trimmed response. ```bash curl https://trmnl.com/api/devices \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` ```json { "data": [ { "id": 65007, "name": "Kitchen", "friendly_id": "ATVDJW", "mac_address": "••:••:••:••:00:04", "firmware_version": "1.6.3", "battery_voltage": 4.0, "refresh_interval": 900 } ] } ``` -------------------------------- ### String and Markdown Filters Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters Examples of pluralize and markdown_to_html filters. The markdown_to_html filter converts markdown to HTML, and can be combined with strip_html to get plain text. ```liquid # pluralize + inflect/humanize {{ "book" | pluralize: 1 }} # => 1 book {{ "book" | pluralize: 2 }} # => 2 books {{ "octopus" | pluralize: 3 }} # => 3 octopi {{ "person" | pluralize: 4 }} # => 4 people # convert markdown into HTML {% assign markdown = "This is *crazy* and [here's a link](https://somewhere.com)." %} {{ markdown | markdown_to_html }} # => This is crazy and here's a link. # optional - pass strip_html to convert to plain text {{ markdown | markdown_to_html | strip_html }} # => This is crazy and here's a link. ``` -------------------------------- ### Result of dot notation output Source: https://help.trmnl.com/en/articles/10671186-liquid-101 The rendered output of the dot notation example. ```text Task "Walk the dog" is due on 2025-02-24 in New York ``` -------------------------------- ### Explore TRMNL CLI commands Source: https://help.trmnl.com/en/articles/17226209-trmnl-cli Use 'trmnl --help' to list all commands, and add '--help' after any command to see its options. Examples include listing devices, updating a device, listing playlist items, and listing plugin settings. ```bash trmnl --help ``` ```bash trmnl list-devices ``` ```bash trmnl update-device 123 'name: Kitchen' ``` ```bash trmnl list-playlist-items ``` ```bash trmnl list-plugin-settings ``` -------------------------------- ### Python Serverless run function using Requests Source: https://help.trmnl.com/en/articles/14130649-serverless Python example that imports the Requests library. The run function fetches a quote and returns a dictionary with the quote key. ```python import requests def run(input): data = requests.get("https://dummyjson.com/quotes?limit=1").json() return {"quote": data["quotes"][0]} ``` -------------------------------- ### curl request to list devices with account API key Source: https://help.trmnl.com/en/articles/11195228-account-api-keys Send the account API key in the Authorization header with Bearer prefix. This example lists your devices, which requires the Read capability. ```bash curl https://trmnl.com/api/devices \ -H "Authorization: Bearer trmnl_xxxxx" ``` -------------------------------- ### Result of basic Liquid output Source: https://help.trmnl.com/en/articles/10671186-liquid-101 The rendered output of the basic Liquid example. ```text Task "Walk the dog" is due on 2025-02-24 ``` -------------------------------- ### Collection filters: group_by, find_by, where_exp Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters Examples of Liquid filters for arrays: group_by groups by a key, find_by finds first match with optional fallback, where_exp filters by expression. The examples show expected output in comments. ```liquid # let people = [{ "name": "Ryan", "age": 35 }, { "name": "Sara", "age": 29 }, { "name": "Jimbob", "age": 29 }] # group_by {{ people | group_by: "age" }} # => {35 => [{"name" => "Ryan", "age" => 35}], 29 => [{"name" => "Sara", "age" => 29}, {"name" => "Jimbob", "age" => 29}]} # find_by , {{ people | find_by: "name", "Ryan" }} # => {"age" => 35, "name" => "Ryan"} # find_by , , {{ people | find_by: "name", "Toby", "Not Found" }} # where_exp # let numbers = [1, 2, 3, 4, 5] {{ numbers | where_exp: "n", "n >= 3" }} # => [3, 4, 5] # let people = [{name: "Ryan", age: 35}, {name: "Liz", age: 25}] {{ people | where_exp: "person", "person.age == 'Ryan' or person.age < 26" }} ``` -------------------------------- ### JSON data structure for a list of tasks Source: https://help.trmnl.com/en/articles/10671186-liquid-101 Example JSON structure with a 'tasks' array containing two task objects, each with name, date, and late properties. This data is used with the for loop example. ```json { "tasks": [ { "name": "Walk the dog", "date": "2025-02-24", "late": true, }, { "name": "Half-Life 3 Release", "date": "2033-10-08", "late": false, } ] } ``` -------------------------------- ### Accessing Global Variables in Polling URL Source: https://help.trmnl.com/en/articles/9510536-private-plugins Example of accessing global variables (such as the installed user's first name) in a Polling URL. Uses the `{{ trmnl.user.first_name }}` interpolation syntax. ```plaintext https://my-service.com/api/v1/products?api_key={{ api_key }}&first_name={{ trmnl.user.first_name }} ``` -------------------------------- ### Ruby Serverless run function with HTTParty Source: https://help.trmnl.com/en/articles/14130649-serverless Use this Ruby example for the Serverless text editor. It requires the 'httparty' gem and includes a 'run' function that returns a hash with input and quotes. ```ruby require 'httparty' def quotes HTTParty.get('https://dummyjson.com/quotes?limit=10')['quotes'] end def run(input) { input:, quotes: } end ``` -------------------------------- ### Complete HTML/CSS example for portrait mode workaround Source: https://help.trmnl.com/en/articles/11396258-portrait-mode A working sample that rotates the content 270 degrees using a style block and includes a sample content div. This is the full markup to paste into TRMNL's live preview editor. ```html
First Time Around

First Time Around is a one-disk DVD by Randy Bachman and Burton Cummings recorded in 2006 at CBC Studios in Toronto, Canada, by CBC. It was originally shown on CBC in April 2006, but was later released as a DVD with extended footage of the concert. The concert has 20 tracks of songs by Bachman-Turner Overdrive, Burton Cummings, The Guess Who and cover versions of artists such as Sting and Jimi Hendrix. It was originally shown on CBC in April 2006, but was later released as a DVD with extended footage of the concert. The concert has 20 tracks of songs by Bachman-Turner Overdrive, Burton Cummings, The Guess Who and cover versions of artists such as Sting and Jimi Hendrix Sting and Jimi Hendrix. It was originally shown on CBC in April 2006, but was later released as a DVD with extended footage of the concert. The concert has 20 tracks of songs by Bachman-Turner Overdrive, Burton Cummings, The Guess Who and cover versions of artists such as Sting and Jimi Hendrix Sting and Jimi Hendrix. It was originally shown on CBC in April 2006, but was later released as a DVD with extended footage of the concert. The concert has 20 tracks of songs by Bachman-Turner Overdrive, Burton Cummings, The Guess Who and cover versions of artists such as Sting and Jimi Hendrix.

``` -------------------------------- ### Currency formatting with number_to_currency Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters Examples of number_to_currency with custom currency units, delimiters, separators, and locale-based formatting. Note that named parameters are not supported, so all preceding arguments must be supplied when overriding later defaults. ```liquid # currency handling {{ 10420 | number_to_currency }} # => $10,420.00 {{ 152350.69 | number_to_currency: '£' }} # => £152,350.69 # optional 2nd + 3rd args delimit + separate {{ 1234.57 | number_to_currency: '£', '.', ',' }} # => £1.234,57 # currency handling with locale instead of currency unit {{ 1234.57 | number_to_currency: 'sv' }} # => 1234.57 kr {{ 1234.57 | number_to_currency: 'sv', '.', ',' }} # => 1.234,57 kr {{ 1234.57 | number_to_currency: 'en-GB' }} # => £1234.57 ``` -------------------------------- ### Notion Database Filter Object Example Source: https://help.trmnl.com/en/articles/11882090-notion Notion filter object example for a database query, using a select property with an equals condition. The filter is built after finding the column type and optionally using dynamic timestamps. ```json { "property": "Author", "select": { "equals": "J. R. R. Tolkien" } } ``` -------------------------------- ### Debug logs example: await outside async function error Source: https://help.trmnl.com/en/articles/14130649-serverless Example error shown when Debug Logs are enabled. It illustrates a SyntaxError from using await outside of an async function. ```javascript let data = await fetch('https://dummyjson.com/quotes?limit=5') ^^^^^ SyntaxError: await is only valid in async functions and the top level bodies of modules ``` -------------------------------- ### Parse lat_lon with Liquid filters Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder Example URL using Liquid 'first' and 'last' filters to split the lat_lon value into separate lat and lon query parameters. ```liquid https://yoururl.com/?lat={{ lat_lon | split: ',' | first }}&lon={{ lat_lon | split: ',' | last }} ``` -------------------------------- ### Node.js v20 Serverless run function with fetch Source: https://help.trmnl.com/en/articles/14130649-serverless Node.js (v20) example that uses the built-in fetch module. Define an asynchronous run function and return an object containing the fetched quote. ```javascript async function run(input) { const res = await fetch("https://dummyjson.com/quotes?limit=1"); const data = await res.json(); return { quote: data.quotes[0] }; } ``` -------------------------------- ### qr_code filter with fixed size options Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters For backwards compatibility, you can use a fixed width/height. This example uses a larger module_size, lower error correction level, and fixed size. ```liquid # A larger module_size, lower error correction "level", and fixed size {{ "https://somewhere.com" | qr_code: 18, "", "fixed" }} ``` -------------------------------- ### Authorization URL with PKCE Source: https://help.trmnl.com/en/articles/17225165-developer-apps Example authorization URL for TRMNL OIDC with PKCE. The link includes response_type, client_id, redirect_uri, scopes, state, and S256 PKCE parameters. The line breaks are for readability; the actual URL should be a single line. ```text https://trmnl.com/oidc/authorize?response_type=code &client_id=YOUR_CLIENT_ID &redirect_uri=https%3A%2F%2Fexample.com%2Fauth%2Ftrmnl%2Fcallback &scope=read%20content &state=abc123 &code_challenge=YOUR_CODE_CHALLENGE &code_challenge_method=S256 ``` -------------------------------- ### Code field configuration Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder Defines a code field with a keyname, field type, rows, name, description, placeholder, and help text. The placeholder is a multi-line JSON example. ```yaml - keyname: json_query field_type: code rows: 8 name: JSON Query description: Provide a raw query placeholder: | { "cannot": "spare a square" } help_text: Learn how to do this here. ``` -------------------------------- ### JSON data structure for task plugin Source: https://help.trmnl.com/en/articles/10671186-liquid-101 Example JSON data representing a task with name, date, late flag, and nested location. ```json { "name": "Walk the dog", "date": "2025-02-24", "late": true, "location": { "city": "New York" } } ``` -------------------------------- ### Liquid conditional output example Source: https://help.trmnl.com/en/articles/10671186-liquid-101 Shows the rendered output of the if/else conditional when the 'late' property is true, displaying the warning emoji. ```text Task "Walk the dog" is due on ⚠️ 2025-02-24 ⚠️ ``` -------------------------------- ### Using Form Field Values in Polling URL and Headers Source: https://help.trmnl.com/en/articles/9510536-private-plugins Shows how to use form field values in the Polling URL and Polling Headers. The static examples use literal values, while the dynamic examples use the `{{ api_key }}` interpolation syntax. ```plaintext # Polling URL - static https://my-store.com/api/v1/products?api_key=qwerty-asdf # Polling URL - using form field values https://my-store.com/api/v1/products?api_key={{ api_key }} # Polling Headers - static authorization=bearer token-here # Polling Headers - using form field values authorization=bearer {{ api_key }} ``` -------------------------------- ### Number formatting with number_with_delimiter Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters Examples of number_with_delimiter with custom delimiters and optional separator for fractional values. The default delimiter is a comma. ```liquid {{ 1234 | number_with_delimiter }} # => 1,234 # custom delimiter (default is comma) {{ 1234 | number_with_delimiter: '.' }} # => 1.234 {{ 1000000 | number_with_delimiter: ' ' }} # => 1 000 000 # optional 2nd arg separates whole number from fractional value {{ 1234.57 | number_with_delimiter: ' ', ',' }} # => 1 234,57 ``` -------------------------------- ### l_date filter examples with strftime syntax and locales Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters Shows how to use the l_date filter to format dates with strftime syntax. Supports a specific locale (e.g., 'ko') or the user's locale via trmnl.user.locale. ```liquid {{ '2025-01-11' | l_date: '%y %b' }} # => 25 Jan # with a specific locale, ex: Korean {{ '2025-01-11' | l_date: '%y %b', 'ko' }} # => 25 1월 # with the user's locale; good for published Plugin Recipes {{ '2025-01-11' | l_date: '%y %b', trmnl.user.locale }} # => 25 1월 ``` -------------------------------- ### Grouped select field YAML Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder Shows how to place a field inside an expandable group using the 'group' parameter. This example uses a select field with Yes/No options and a default of 'no'. ```yaml - keyname: include_past_events name: Include past events? group: "Event Display" field_type: select options: - 'Yes' - 'No' default: 'no' ``` -------------------------------- ### Use where filter to select late tasks Source: https://help.trmnl.com/en/articles/10671186-liquid-101 Rewrites the previous late-task example using the where filter to filter the tasks list by the 'late' property being true. ```Liquid {% assign late_tasks = tasks | where: "late", true %} {% for task in late_tasks %}

Task "{{ task.name }}" is due on {{ task.date }}

{% endfor %} ``` -------------------------------- ### Display saved state in markup Source: https://help.trmnl.com/en/articles/16777795-saved-state Saved state is available in your markup as trmnl.state, so you do not need to return the same data twice. This example iterates over trmnl.state.history. ```liquid {% for point in trmnl.state.history %} {{ point.value }} {% endfor %} ``` -------------------------------- ### Serverless Node.js transform counting 1-100 Source: https://help.trmnl.com/en/articles/16777795-saved-state A simple Serverless script for Node/Javascript that sequentially counts from 1-100 and then loops, always storing the number in trmnl.state. Note: This example does not return any existing merge variables. ```javascript function transform(input) { var num = (input.trmnl.state && input.trmnl.state.number) || 0; entry++ if (num > 100) { entry = 1 } return { trmnl_state: { number: num } }; } ``` -------------------------------- ### Generate simple random number between 0 and 9 Source: https://help.trmnl.com/en/articles/10693981-advanced-liquid Uses the date filter with '%N' to get nanoseconds and modulo 10 to produce a random integer from 0 to 9. ```Liquid {% assign random_number = "now" | date: "%N" | modulo: 10 %} ``` -------------------------------- ### Polling Headers JSON Example Source: https://help.trmnl.com/en/articles/9510536-private-plugins Shows the JSON object that results from entering polling headers as `Name: Value` lines, one per line. Use this format to add custom headers to your Polling URL. ```json { "authorization":"bearer xxx", "content-type":"application/json" } ``` -------------------------------- ### Add MCP server in Claude Code Source: https://help.trmnl.com/en/articles/17432548-mcp-server Run this command in Claude Code to add the TRMNL MCP server using your generated key. Replace YOUR_KEY with the actual key that starts with ps_mcp_. ```bash claude mcp add --transport http trmnl --header "Authorization: Bearer YOUR_KEY" ``` -------------------------------- ### Add TRMNL MCP server to Claude Code via CLI Source: https://help.trmnl.com/en/articles/17432548-mcp-server Run this command to register the TRMNL MCP server for the current folder only. Add --scope user to use it everywhere. The Claude Code desktop app reads the same setup. ```bash claude mcp add --transport http trmnl https://trmnl.com/mcp ``` -------------------------------- ### Select random element from an array using random_number Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters This example splits a CSV string into an array, calculates the maximum index (size minus 1), generates a random index using `random_number` with only the upper limit, and selects the element. The comment notes that size is 6 but indices are 0-5, so 1 must be subtracted. ```liquid {%liquid assign csv_string = "zero,alpha,bravo,charlie,delta,echo" assign my_array = csv_string | split: ',' ### size = 6, but we need 0-5 so we have to subtract 1 assign max = my_array.size | minus: 1 assign rnum = "" | random_number: max | plus: 0 assign selection = my_array[rnum] %} ``` -------------------------------- ### Custom field with external link Source: https://help.trmnl.com/en/articles/11395668-recipe-form-fields-best-practices Use HTML in the description to embed a clickable link, which shortens text, opens in a new tab, and applies the underline CSS class. Remove 'https://' from the link if it's just an example and should not be clickable. ```yaml - keyname: api_key name: API Key description: Get your API key from Server Settings. placeholder: asdf-fdsa field_type: string optional: true ``` -------------------------------- ### l_word filter examples for translating common words Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters Shows how to use the l_word filter to translate common words like 'today' and 'tomorrow' to another language, either with a specific locale or the user's locale. ```liquid

{{ "today" | l_word: 'es-ES' }}

# => hoy {% assign day = "tomorrow" %}

{{ day | l_word: trmnl.user.locale }}

# => 내일 ``` -------------------------------- ### Shopify OAuth authorization URL Source: https://help.trmnl.com/en/articles/9502507-shopify Replace YOUR-STORE with your MyShopify admin subdomain (e.g., usetrmnl), XXX with the Client ID, and YYY with the Secret from the Shopify Dev Dashboard. Paste the updated URL into a browser tab to start the OAuth authorization flow. ```text https://YOUR-STORE.myshopify.com/admin/oauth/authorize?client_id=XXX&client_secret=YYY&scopes=read_orders&redirect_uri=https://example.com ``` -------------------------------- ### Date Filters: days_ago and ordinalize Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters Examples of days_ago and ordinalize filters. days_ago returns a date string representing the given number of days before now. ordinalize accepts a date string and a strftime argument with <> placeholder, which is replaced by the ordinal (e.g., 2nd). ```liquid {{ 7 | days_ago }} => yyyy-mm-dd representing 7 days before now # oridinalize a day, e.g. 1 => 1st, 30 => 30th # accepts a date string with strftime argument that includes <>, which will be substituted by the ordinal itself {{ "2025-10-02" | ordinalize: "%A, %B <>, %Y" }} # => Thursday, October 2nd, 2025 {{ "2025-12-31 16:50:38 -0400" | ordinalize: "%A, %b <>" }} # => Wednesday, Dec 31st ``` -------------------------------- ### Kindle 6th Gen Paperwhite adapter Source: https://help.trmnl.com/en/articles/11547008-device-model-faq Example of an official model adapter for the Kindle 6th Gen Paperwhite. This structure is used inside TRMNL's screen generation engine to define hardware attributes like resolution, color depth, rotation, and format. ```ruby { id: 5, keyname: 'amazon_kindle_paperwhite_6th_gen', name: 'Amazon Kindle PW 6th Gen', width: 1024, height: 768, colour_depth: 8, colours: 256, scale_factor: 1, rotate: 90, offset_x: 0, offset_y: 0, format: 'png', device_type: :kindle } ``` -------------------------------- ### Conditional visibility with hidden flag Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder Shows how to hide fields based on the selected value of a 'strategy' select field. The `when` value matches the field's value, and `hidden` lists fields to remove. This example is from the Private Plugin editor. ```yaml - keyname: strategy field_type: select options: - Polling - Webhook - Static default: polling conditional_validation: - when: polling hidden: - static_data - webhook_url - when: webhook hidden: - polling_url - polling_verb - polling_headers - polling_body - static_data - when: static hidden: - polling_url - polling_verb - polling_headers - polling_body - webhook_url ``` -------------------------------- ### xhrSelectSearch field configuration Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder YAML configuration for an xhrSelectSearch field. The endpoint receives a POST request by default; set http_verb to change it. Search query is sent in a 'query' body parameter for POST, or in the query string for GET. Set exclude_csrf_token: true to disable the X-CSRF-Token header. ```yaml - keyname: project name: Project field_type: xhrSelectSearch endpoint: https://trmnl.com/custom_plugin_example_xhr_select_search.json ``` -------------------------------- ### GET Webhook URL Source: https://help.trmnl.com/en/articles/17434705-plugin-strategies GET the Webhook URL to retrieve the currently stored data. This is useful for checking your work. ```APIDOC ## [GET] Webhook URL ### Description GET the Webhook URL to retrieve the currently stored data. This is useful for checking your work. ### Method GET ### Endpoint Webhook URL (from plugin settings) ### Parameters None documented. ### Request Example No request body. ### Response #### Success Response (200) - **stored data** (object) - The current stored data. #### Response Example No response example documented. ``` -------------------------------- ### Custom field with HTML link in description Source: https://help.trmnl.com/en/articles/11395668-recipe-best-practices Example of a custom form field definition that uses an HTML link in the description to point users to an external location. The link opens in a new tab and is more obviously clickable with the underline CSS class. Remove https:// from the beginning of the link if it should not be clickable. ```yaml - keyname: api_key name: API Key description: Get your API key from Server Settings. placeholder: asdf-fdsa field_type: string optional: true ``` -------------------------------- ### Flash TRMNL OG firmware with esptool Source: https://help.trmnl.com/en/articles/10271569-manually-flash-firmware Flash the TRMNL OG firmware via CLI. Requires the esptool.py dependency and a device chip set like ESP32. Assumes port COM3 and a file named firmware.bin in the current directory. ```bash esptool.py --chip esp32 --port COM3 --baud 115200 write_flash 0x0000 firmware.bin ``` -------------------------------- ### Define a custom field in YAML Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder Define a custom form field in your plugin's YAML configuration. The example defines a field with keyname `api_key`, type `string`, name `API Key`, and a description. The `keyname` is used to reference the field's value in polling headers or URLs. ```yaml - keyname: api_key field_type: string name: API Key description: Find this at my-server.com/settings ``` -------------------------------- ### Troubleshooting: number_to_currency with value--tnums Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters Combining filters with framework handlers like Value Formatting may produce unexpected results. This example shows `{{ 10420.00 | number_to_currency }}` inside a span with class `value--tnums`, which yields $10,420 without trailing `.00` because `value--tnums` simplifies whole numbers. ```html {{ 10420.00 | number_to_currency }} ``` -------------------------------- ### Help text for multi-select field Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder Recommended help_text for multi-select fields, explaining how to select and deselect multiple options using keyboard shortcuts. ```html Use ⌘+click or ctrl+click to select and deselect multiple options. ``` -------------------------------- ### Sign in to TRMNL CLI Source: https://help.trmnl.com/en/articles/17226209-trmnl-cli Run any command that touches your account, such as 'trmnl list-devices', to trigger browser-based sign-in. The browser opens trmnl.com and asks for permissions; read, content, and devices are ticked by default, while delete, profile, and apps are not. ```bash trmnl list-devices ``` -------------------------------- ### Generate PKCE verifier and challenge Source: https://help.trmnl.com/en/articles/17225165-developer-apps Generates a PKCE verifier and challenge pair using OpenSSL. The first command creates a 64-character random verifier, and the second computes the S256 challenge. Use these values in the authorization URL's code_challenge and code_challenge_method parameters. ```bash verifier=$(openssl rand -base64 48 | tr -d '=+/\n' | cut -c1-64) challenge=$(printf %s "$verifier" | openssl dgst -binary -sha256 | openssl base64 | tr '+/' '-_' | tr -d '=\n') ``` -------------------------------- ### Basic Liquid output with {{ name }} and {{ date }} Source: https://help.trmnl.com/en/articles/10671186-liquid-101 HTML snippet that outputs the name and date properties from the JSON data. ```html

Task "{{ name }}" is due on {{ date }}

``` -------------------------------- ### Chain filters to calculate a percentage Source: https://help.trmnl.com/en/articles/10671186-liquid-101 Demonstrates chaining filters: divided_by, times, and round to calculate a percentage from two numbers. ```Liquid {% assign value = 20.0 %} {% assign total = 50.0 %} {% assign percentage = value | divided_by: total | times: 100 | round %}

{{ value }} is {{ percentage }}% of {{ total }}

``` -------------------------------- ### Base64 encoded PNG data URI Source: https://help.trmnl.com/en/articles/12391781-creating-inline-images-for-plugins Example of a Base64-encoded PNG data URI generated by an external encoder, to be placed inside an img src attribute. ```Text data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB/klEQVRIie1Vu6riUBRdeZhEEEXUKkEUA2nEIq2gYJFGwZ+wFMTKQqyudoKF/2ClWCpW+QE70coXtulEFDHZt7rOSKJzbyEDw2zYzdqPddZmn3MYIiK80dh3Nv83CHjHcd5K4KnANE3IsgxZlmHb9ssGyWQSsixjOp16xnkv0OfzIRKJAABWqxWCwSDi8fhDzuFwwPF4RDgcxu12gyAI3iewbZteuSAIZBiGCy+Xy8RxHJ3P55f1ngp2ux2GwyEAwHEc7Pd7dLvdh5z1eg0iQq/XA8/zKJfLUFX1ewomkwmxLPtt5ziOxuOxpwIXwWg0okwmQyzLUrVaJdM0KZFIuJp2Oh0yTZNEUSSWZSmdTtNgMHg+osvlAtu2sd1usVgsAACKokDXdUiS5FKeSqWg6zoYhgEALJdLbDYbnE4ncBz3q+aLqVgs/mgsr7xQKNwVuO6BqqrI5XLeK/fCstksNE1z4S4CwzDQaDR+TFCv11EqlVy455r+bv1+Hyz7/Mmq1Wq43W5P438kqFQq4HnvNCJCvV5/Wc/Ytk0AYFkWLpcLAoEAJEmCZVn4+PjAbDa7JzebTUiShFardcfy+Tza7TYikQiu1yuOxyNEUUQsFntUEI1GH5gVRYGmaZjP5/j69Px+P0RRvL9TAKBpGhRFucdDoZC3gncZ8/9P/usEn7TmdRqwWqbRAAAAAElFTkSuQmCC ``` -------------------------------- ### Dynamic Polling URL with Liquid Date Interpolation Source: https://help.trmnl.com/en/articles/12689499-dynamic-polling-urls Shows how to use Liquid templating to insert today's date into a polling URL. The comment explains the conversion and the result line shows the expected output. ```text # convert "now" time helper into YYYY-MM-DD https://reddit.com/api/posts.json?since=##{{ "now" | date: "%Y-%m-%d" }} # result https://reddit.com/api/posts.json?since=2025-10-29 ``` -------------------------------- ### Dynamic Polling URL with Custom Form Field Source: https://help.trmnl.com/en/articles/12689499-dynamic-polling-urls Example of a polling URL that interpolates a user-provided value from a custom form field. The field must have the keyname 'api_key'. ```text https://twitter.com/api/tweets?access_token=##{{ api_key }} ``` -------------------------------- ### split and sample Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters Splits a comma-separated string into an array and then samples a single random value. Optionally strips whitespace from the result. ```APIDOC ## split and sample ### Description Splits a comma-separated string into an array and then samples a single random value. Optionally strips whitespace from the result. ### Syntax `{{ string | split: ',' | sample }}` `{{ string | split: ',' | sample | strip }}` ### Parameters - **string** (string) - Required - The comma-separated string to split. ### Example ```liquid {% assign isbns = "9781473205321,9781473205338,9781473200203, 9781473200104" %} {{ isbns | split: ',' | sample }} {{ isbns | split: ',' | sample | strip }} ``` ``` -------------------------------- ### plugin_instance_select field configuration Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder YAML configuration for a plugin_instance_select field. Requires the recipe to be forked. Only plugins currently in a playlist or mashup are shown. Works only with the 'plugin_merge' strategy. The selected value will be in the format {{ weather_source }}. ```yaml - keyname: weather_source field_type: plugin_instance_select name: Weather Data Source plugin_keyname: weather description: Select which Weather plugin instance to pull data from help_text: Only your active Weather plugins are shown. ``` -------------------------------- ### Left-pad strings with prepend and slice filters Source: https://help.trmnl.com/en/articles/10693981-advanced-liquid Uses the prepend and slice filters to left-pad numbers with zeros, e.g., converting 1:3 to 01:03. Adjust padding characters and slice lengths to modify. ```liquid {%liquid # original values (suppose these were calculated) assign hours = 1 assign minutes = 3 # left-padding with zero assign hours = hours | prepend: "00" | slice: -2, 2 assign minutes = minutes | prepend: "00" | slice: -2, 2 %} ``` -------------------------------- ### MCP server address for other clients Source: https://help.trmnl.com/en/articles/17432548-mcp-server Use this URL as the server address in other MCP clients. Replace YOUR_KEY with your actual key. ```text https://trmnl.com/mcp?api_key=YOUR_KEY ``` -------------------------------- ### Use random_number inline in a URL Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters You can use the filter directly in a URL without assigning a variable, for example in a polling URL: `https://some-api.com/jokes/{{ "" | random_number: 1, 1000 }}`. ```liquid https://some-api.com/jokes/{{ "" | random_number: 1, 1000 }} ``` -------------------------------- ### Run TRMNL HA as a Docker container Source: https://help.trmnl.com/en/articles/13281388-home-assistant-screenshot Use this command to run TRMNL HA as a standalone Docker container when you don't have the add-on store (e.g., HA Container). Replace YOUR_IP and your_token with your Home Assistant IP and access token. For ARM64 devices, use the aarch64 image instead of amd64. ```bash docker run -d --name trmnl-ha \ -e HOME_ASSISTANT_URL=http://YOUR_IP:8123 \ -e ACCESS_TOKEN=your_token \ -p 10000:10000 \ ghcr.io/usetrmnl/trmnl-ha-amd64:latest ``` -------------------------------- ### Styling QR codes with CSS class qr-code Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters TRMNL automatically adds class 'qr-code' to QR codes. This example positions the QR code in the top right corner of the screen using CSS. ```css {{ "https://trmnl.ink" | qr_code: 3 }} ``` -------------------------------- ### Split comma-separated string and sample randomly Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters Shows how to split a comma-separated string into an array and sample a single value randomly. Includes a variant that strips whitespace from the sampled value. ```liquid # let isbns = "9781473205321,9781473205338,9781473200203, 9781473200104" {{ isbns | split: ',' | sample }} # removing whitespace # let isbns = "9781473205321, 9781473205338, 9781473200203, 9781473200104" {{ isbns | split: ',' | sample | strip }} ``` -------------------------------- ### append_random Filter and Usage Source: https://help.trmnl.com/en/articles/10347358-custom-plugin-filters The append_random filter generates a unique suffix for HTML element IDs to avoid conflicts in mashups. The example shows generating a chart ID and then using it in a div and JavaScript call. ```liquid {% assign chart_id = "chart-" | append_random %} # => "chart-q7x1" ``` ```html
Highcharts.chart("{{ chart_id }}") ``` -------------------------------- ### Wrap JS in DOMContentLoaded for client-side mutations Source: https://help.trmnl.com/en/articles/10693981-advanced-liquid Recommended pattern for client-side mutations due to rendering logic. More reliable than window.onload. ```javascript document.addEventListener("DOMContentLoaded", function(e) { // code goes here }); ``` -------------------------------- ### author_bio field definition example Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder YAML definition for an author_bio field with optional properties. Must be indented by 2 spaces with a leading hyphen. Categories are limited to an approved list and cached for ~2 hours. ```yaml - keyname: doesnt_matter name: About This Plugin category: life,news field_type: author_bio description: Dad Jokes Daily™ was created by Abraham, father to many. github_url: https://github.com/father-abraham learn_more_url: https://www.bible.com/ email_address: fatherabe@hotmail.net youtube_url: https://www.youtube.com/@useTRMNL ``` -------------------------------- ### Access custom field values in Markup Editor Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder Use the `trmnl.plugin_settings.custom_fields_values` variable in your plugin's markup to access the submitted form values as a hash. The example shows a hash with keys `password` and `lookback_period`. ```markup {{ trmnl.plugin_settings.custom_fields_values }} # => { "password": "secret", "lookback_period": "7" } ``` -------------------------------- ### Integer division truncates to 0 Source: https://help.trmnl.com/en/articles/10693981-advanced-liquid Demonstrates that dividing two integers yields an integer result, so 1 divided by 2 displays 0. ```Liquid {{ 1 | divided_by: 2 }} ``` -------------------------------- ### Plugin ZIP file contents Source: https://help.trmnl.com/en/articles/10542599-importing-and-exporting-private-plugins Flat list of files required in the plugin ZIP archive. settings.yml is required; the .liquid files are optional templates for each viewport. ```plaintext settings.yml full.liquid half_horizontal.liquid half_vertical.liquid quadrant.liquid ``` -------------------------------- ### Time field configuration Source: https://help.trmnl.com/en/articles/10513740-custom-plugin-form-builder Defines a time field with a keyname, field type, name, description, and help text. The help text indicates it is optional and applies to week view only. ```yaml - keyname: scroll_time field_type: time name: Fixed start time? description: By default, the week's earliest event time will be used. help_text: Optional. Applies to week view only. ``` -------------------------------- ### Cursor MCP configuration for TRMNL Source: https://help.trmnl.com/en/articles/17432548-mcp-server Add this JSON to ~/.cursor/mcp.json and restart Cursor to connect the TRMNL MCP server. The server URL is https://trmnl.com/mcp. ```json {"mcpServers": {"trmnl": {"url": "https://trmnl.com/mcp"}}} ``` -------------------------------- ### Liquid for loop to iterate over tasks Source: https://help.trmnl.com/en/articles/10671186-liquid-101 Iterates over the 'tasks' list and prints each task's name and date. The loop variable 'task' holds the current task's properties. ```liquid {% for task in tasks %}

Task "{{ task.name }}" is due on {{ task.date }}

{% endfor %} ```