diff --git a/.gitbook/assets/Screenshot 2024-03-21 at 4.25.22 PM.png b/.gitbook/assets/Screenshot 2024-03-21 at 4.25.22 PM.png new file mode 100644 index 0000000..ec25577 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-03-21 at 4.25.22 PM.png differ diff --git a/.gitbook/assets/Screenshot 2024-03-21 at 7.30.43 PM.png b/.gitbook/assets/Screenshot 2024-03-21 at 7.30.43 PM.png new file mode 100644 index 0000000..30a6894 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-03-21 at 7.30.43 PM.png differ diff --git a/.gitbook/assets/Screenshot 2024-03-21 at 8.04.40 PM.png b/.gitbook/assets/Screenshot 2024-03-21 at 8.04.40 PM.png new file mode 100644 index 0000000..a1375da Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-03-21 at 8.04.40 PM.png differ diff --git a/.gitbook/assets/Screenshot 2024-06-19 at 1.00.01 AM.png b/.gitbook/assets/Screenshot 2024-06-19 at 1.00.01 AM.png new file mode 100644 index 0000000..a5e7541 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-06-19 at 1.00.01 AM.png differ diff --git a/.gitbook/assets/Screenshot 2024-06-19 at 1.08.44 AM.png b/.gitbook/assets/Screenshot 2024-06-19 at 1.08.44 AM.png new file mode 100644 index 0000000..c25d986 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-06-19 at 1.08.44 AM.png differ diff --git a/.gitbook/assets/Screenshot 2024-06-19 at 1.13.37 AM.png b/.gitbook/assets/Screenshot 2024-06-19 at 1.13.37 AM.png new file mode 100644 index 0000000..4520678 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-06-19 at 1.13.37 AM.png differ diff --git a/.gitbook/assets/Screenshot 2024-06-19 at 1.18.15 AM.png b/.gitbook/assets/Screenshot 2024-06-19 at 1.18.15 AM.png new file mode 100644 index 0000000..e317dab Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-06-19 at 1.18.15 AM.png differ diff --git a/.gitbook/assets/Screenshot 2024-06-19 at 1.26.54 AM.png b/.gitbook/assets/Screenshot 2024-06-19 at 1.26.54 AM.png new file mode 100644 index 0000000..9b838b0 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-06-19 at 1.26.54 AM.png differ diff --git a/.gitbook/assets/Screenshot 2024-06-19 at 1.38.53 AM.png b/.gitbook/assets/Screenshot 2024-06-19 at 1.38.53 AM.png new file mode 100644 index 0000000..17a4773 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-06-19 at 1.38.53 AM.png differ diff --git a/.gitbook/assets/Screenshot 2024-06-19 at 1.40.03 AM.png b/.gitbook/assets/Screenshot 2024-06-19 at 1.40.03 AM.png new file mode 100644 index 0000000..af2d190 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-06-19 at 1.40.03 AM.png differ diff --git a/.gitbook/assets/Screenshot 2024-09-06 at 3.32.53 PM.png b/.gitbook/assets/Screenshot 2024-09-06 at 3.32.53 PM.png new file mode 100644 index 0000000..fb154eb Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-09-06 at 3.32.53 PM.png differ diff --git a/.gitbook/assets/Screenshot 2024-09-06 at 3.35.01 PM.png b/.gitbook/assets/Screenshot 2024-09-06 at 3.35.01 PM.png new file mode 100644 index 0000000..dac9001 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-09-06 at 3.35.01 PM.png differ diff --git a/.gitbook/assets/Screenshot 2024-09-06 at 3.36.35 PM.png b/.gitbook/assets/Screenshot 2024-09-06 at 3.36.35 PM.png new file mode 100644 index 0000000..50a775f Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-09-06 at 3.36.35 PM.png differ diff --git a/.gitbook/assets/Screenshot_2024-09-18_at_23.53.24.png b/.gitbook/assets/Screenshot_2024-09-18_at_23.53.24.png new file mode 100644 index 0000000..a4bddd7 Binary files /dev/null and b/.gitbook/assets/Screenshot_2024-09-18_at_23.53.24.png differ diff --git a/.gitbook/assets/TRMNL architecture overview.png b/.gitbook/assets/TRMNL architecture overview.png new file mode 100644 index 0000000..b1420f4 Binary files /dev/null and b/.gitbook/assets/TRMNL architecture overview.png differ diff --git a/.gitbook/assets/TRMNL-force-refresh-param.png b/.gitbook/assets/TRMNL-force-refresh-param.png new file mode 100644 index 0000000..09cbb0c Binary files /dev/null and b/.gitbook/assets/TRMNL-force-refresh-param.png differ diff --git a/.gitbook/assets/TRMNL-quickstart-markup.png b/.gitbook/assets/TRMNL-quickstart-markup.png new file mode 100644 index 0000000..eb49635 Binary files /dev/null and b/.gitbook/assets/TRMNL-quickstart-markup.png differ diff --git a/.gitbook/assets/chart-example.bmp b/.gitbook/assets/chart-example.bmp new file mode 100644 index 0000000..2174316 Binary files /dev/null and b/.gitbook/assets/chart-example.bmp differ diff --git a/.gitbook/assets/custom-plugin-quickstart-example-0.0.5-css.png b/.gitbook/assets/custom-plugin-quickstart-example-0.0.5-css.png new file mode 100644 index 0000000..e4ca05a Binary files /dev/null and b/.gitbook/assets/custom-plugin-quickstart-example-0.0.5-css.png differ diff --git a/.gitbook/assets/grayscale (1).png b/.gitbook/assets/grayscale (1).png new file mode 100644 index 0000000..e0ef99b Binary files /dev/null and b/.gitbook/assets/grayscale (1).png differ diff --git a/.gitbook/assets/grayscale-12.png b/.gitbook/assets/grayscale-12.png new file mode 100644 index 0000000..849b673 Binary files /dev/null and b/.gitbook/assets/grayscale-12.png differ diff --git a/.gitbook/assets/grayscale.png b/.gitbook/assets/grayscale.png new file mode 100644 index 0000000..c5400f4 Binary files /dev/null and b/.gitbook/assets/grayscale.png differ diff --git a/.gitbook/assets/image (1).png b/.gitbook/assets/image (1).png new file mode 100644 index 0000000..4b67b97 Binary files /dev/null and b/.gitbook/assets/image (1).png differ diff --git a/.gitbook/assets/image (2).png b/.gitbook/assets/image (2).png new file mode 100644 index 0000000..6f9a691 Binary files /dev/null and b/.gitbook/assets/image (2).png differ diff --git a/.gitbook/assets/image (3).png b/.gitbook/assets/image (3).png new file mode 100644 index 0000000..d94cbda Binary files /dev/null and b/.gitbook/assets/image (3).png differ diff --git a/.gitbook/assets/image (4).png b/.gitbook/assets/image (4).png new file mode 100644 index 0000000..d94cbda Binary files /dev/null and b/.gitbook/assets/image (4).png differ diff --git a/.gitbook/assets/image.png b/.gitbook/assets/image.png new file mode 100644 index 0000000..3f5763a Binary files /dev/null and b/.gitbook/assets/image.png differ diff --git a/.gitbook/assets/plugin-uuid-example.png b/.gitbook/assets/plugin-uuid-example.png new file mode 100644 index 0000000..427285a Binary files /dev/null and b/.gitbook/assets/plugin-uuid-example.png differ diff --git a/.gitbook/assets/trmnl-data-mode-edit-markup.png b/.gitbook/assets/trmnl-data-mode-edit-markup.png new file mode 100644 index 0000000..8703264 Binary files /dev/null and b/.gitbook/assets/trmnl-data-mode-edit-markup.png differ diff --git a/.gitbook/assets/trmnl-line-chart-example.png b/.gitbook/assets/trmnl-line-chart-example.png new file mode 100644 index 0000000..aab59d4 Binary files /dev/null and b/.gitbook/assets/trmnl-line-chart-example.png differ diff --git a/.gitbook/assets/trmnl-markup-editor-live-preview.png b/.gitbook/assets/trmnl-markup-editor-live-preview.png new file mode 100644 index 0000000..370d5c7 Binary files /dev/null and b/.gitbook/assets/trmnl-markup-editor-live-preview.png differ diff --git a/.gitbook/assets/trmnl-merger-variables-calendar-raw-data.png b/.gitbook/assets/trmnl-merger-variables-calendar-raw-data.png new file mode 100644 index 0000000..60c1aab Binary files /dev/null and b/.gitbook/assets/trmnl-merger-variables-calendar-raw-data.png differ diff --git a/.gitbook/assets/trmnl-playlist-drag-drop.png b/.gitbook/assets/trmnl-playlist-drag-drop.png new file mode 100644 index 0000000..01accb6 Binary files /dev/null and b/.gitbook/assets/trmnl-playlist-drag-drop.png differ diff --git a/.gitbook/assets/trmnl-playlist-group-example.png b/.gitbook/assets/trmnl-playlist-group-example.png new file mode 100644 index 0000000..dadafed Binary files /dev/null and b/.gitbook/assets/trmnl-playlist-group-example.png differ diff --git a/.gitbook/assets/trmnl-plugin-form.png b/.gitbook/assets/trmnl-plugin-form.png new file mode 100644 index 0000000..6c3affe Binary files /dev/null and b/.gitbook/assets/trmnl-plugin-form.png differ diff --git a/.gitbook/assets/trmnl-private-plugin-webhook-url.png b/.gitbook/assets/trmnl-private-plugin-webhook-url.png new file mode 100644 index 0000000..2d8876b Binary files /dev/null and b/.gitbook/assets/trmnl-private-plugin-webhook-url.png differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..d86f4cd --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +--- +description: >- + Welcome to TRMNL. Here you can learn how to build plugins, connect your own + hardware, and more. +layout: + title: + visible: true + description: + visible: true + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: false +--- + +# 👋 Overview + +{% content-ref url="how-it-works.md" %} +[how-it-works.md](how-it-works.md) +{% endcontent-ref %} + +{% content-ref url="private-plugins/templates.md" %} +[templates.md](private-plugins/templates.md) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..3cd2dde --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,40 @@ +# Table of contents + +* [👋 Overview](README.md) +* [How it Works](how-it-works.md) + +## Private Plugins + +* [Screen Templating](private-plugins/templates.md) +* [🖼️ Plugin Templating (DEPRECATED, hidden)](private-plugins/templates-1.md) +* [Screen Templating (Graphics)](private-plugins/templates-advanced.md) +* [Create a screen](private-plugins/create-a-screen.md) + +## DIY TRMNL (Advanced) + +* [Introduction](diy/introduction.md) +* [BYOD](diy/byod.md) +* [BYOD/S](diy/byod-s.md) +* [BYOS](diy/byos.md) + +## Plugin Marketplace + +* [Introduction](plugin-marketplace/introduction.md) +* [Plugin Creation](plugin-marketplace/plugin-creation.md) +* [Plugin Installation Flow](plugin-marketplace/plugin-installation-flow.md) +* [Plugin Management Flow](plugin-marketplace/plugin-management-flow.md) +* [Plugin Screen Generation Flow](plugin-marketplace/plugin-screen-generation-flow.md) +* [Plugin Uninstallation Flow](plugin-marketplace/plugin-uninstallation-flow.md) +* [Going Live](plugin-marketplace/going-live.md) + +## Private API + +* [Introduction](private-api/introduction.md) +* [Fetch Screen Content](private-api/fetch-screen-content.md) +* [Fetch Plugin Content](private-api/fetch-plugin-content.md) + +## Partners API + +* [Introduction](partners-api/introduction.md) +* [Getting Started](partners-api/getting-started.md) +* [Provisioning Devices](partners-api/provisioning-devices.md) diff --git a/diy/byod-s.md b/diy/byod-s.md new file mode 100644 index 0000000..2b395a2 --- /dev/null +++ b/diy/byod-s.md @@ -0,0 +1,53 @@ +--- +description: Bring your own device, and build your own server for the device to ping. +--- + +# BYOD/S + +In the BYOD/S model, the only TRMNL IP is our [open source firmware](https://github.com/usetrmnl/firmware). Technically we don't owe you any explanation to get up and running, but we'll do it anyway. ;) + +### Device setup + +See our [BYOD guide](byod.md) for instructions to build a device that's compatible with our firmware. + +### Server quickstart + +The TRMNL web server generates bitmap images. When a device pings our web server, the next-in-queue image is shared as an absolute URL inside a JSON response like this: + +``` +{ + "image_url"=>"https://trmnl.s3.us-east-2.amazonaws.com/path-to-img.bmp" +} +``` + +You can demo this process from the command line by installing [ImageMagick](https://en.wikipedia.org/wiki/ImageMagick), then invoking `convert`: + +``` +convert regular_img.png -depth 1 eink_compatible.bmp +``` + +With this in mind, building your own server simply necessitates creating an endpoint that responds with links to firmware-compatible Bitmap images. + +Assuming you've set up a device, simply... + +1. change the base URL to your own server or local network from the WiFi Captive Portal +2. mimic the `api/setup` and `api/display` endpoints to respond per the [firmware readme](https://github.com/usetrmnl/firmware) +3. profit + +### Other infrastructure + +In the quickstarter above we glossed over a critical element: "next-in-queue" images. + +At TRMNL we use a Playlists table to manage the ordering of plugin instances, so that users can drag/drop different screen content in any sequence they like. + +

Drag/Drop Playlists UI

+ +Each item in a device's Playlist is an instance\* of a Plugin, something we call a PluginSetting. + +This keeps our Plugins table immutable, for example our Google Calendar record contains just name, icon, etc. But details about an actual connection to Google Calendar are stored inside a PluginSetting record. Keep this in mind as you build your own server -- do you want to allow multiple connections to the same parent plugin? + +TRMNL also supports PlaylistGroups. These are parent objects to playlists, and they simply act as buckets to which playlist items should be rendered on a device during any given time period. + +

Playlist Groups in action

+ +This is another feature to consider building on your own implementation, but does not need to be considered at the device or firmware level. diff --git a/diy/byod.md b/diy/byod.md new file mode 100644 index 0000000..a7cbbfe --- /dev/null +++ b/diy/byod.md @@ -0,0 +1,35 @@ +--- +description: Bring your own device to TRMNL. +--- + +# BYOD + +Before diving into "how," it's worth mentioning that **the investment required to build your own device will be greater than our retail price**. + +Making your own TRMNL from scratch is not an economically rational decision, but rather a labor of love. Our own team learned this the ~~hard~~ fun way while building v1 over 7 months, from Dec 2023 to July 2024. + +Here's what you can expect to spend per component: + +* Battery, $5 (unnecessary if you prefer plugged in) +* EPD screen, $65 (see the Waveshare 7.5" on Amazon) +* Microcontroller, $3-50 (depends if you build/solder yourself or leverage a PCB prototyper) +* Enclosure/case, $3-20 (design + 3D print yourself or use a print farm) + +If you know what you're doing and just need some firmware or a server client, here ya go: + +* Firmware: [https://github.com/usetrmnl/firmware](https://github.com/usetrmnl/firmware) +* Server (multiple options): [https://docs.usetrmnl.com/go/diy/byos](https://docs.usetrmnl.com/go/diy/byos) + +When you're ready to build plugins: + +1. Buy access to the TRMNL web app + API: [https://shop.usetrmnl.com/products/byod](https://shop.usetrmnl.com/products/byod) +2. Activate a virtual device: [https://usetrmnl.com/claim-a-device](https://usetrmnl.com/claim-a-device) +3. Plug your TRMNL API key into your DIY device firmware +4. Stay focused + +Email team@usetrmnl.com if you have any questions. + +### Hardware requirements + +(_Coming Soon_) + diff --git a/diy/byos.md b/diy/byos.md new file mode 100644 index 0000000..8c7951a --- /dev/null +++ b/diy/byos.md @@ -0,0 +1,100 @@ +--- +description: Buy a TRMNL device, then point it at your own server. +--- + +# BYOS + +First, purchase a TRMNL from our [home page](https://usetrmnl.com). Then choose a BYOS implementation for your stack. Our reference implementation is Terminus (Hanami) and we recommended you get started there but you can also choose from other languages/frameworks as well. + +**Why BYOS?** + +TRMNL intends to ensure that **every device is un-brickable and can run with zero external dependencies**. + +### Implementations + +We support multiple implementations developed by us and the community at large. The goal isn't for BYOS to match parity with our hosted solution but to provide enough of a pleasant solution for your own customized experience. There are trade offs either way but we've got you covered for whatever path you wish to travel. + +**Legend** + +Use this legend to understand the matrix of features below. + +* 🟢 Supported. +* 🟡 Partially supported. +* 🔴 Not supported or not implemented. +* ⚫️ Archived with minimal maintenance support. +* ⚪️ Unknown. + +**Matrix** + +Below is a list of all implementations in various languages/frameworks you can use to self-host and manage your devices with: + +
ImplementationDashboardAuto-ProvisioningDevicesJSON Data APIImage PreviewsPlaylistsPluginsRecipesDockerTest SuiteMaintainedSemantic Versioning
TRMNL Terminus (Ruby + Hanami)🟢🟢🟢🟢🟢🟡🟢🟢🟢🟢🟢🟢
TRMNL (Elixir + Phoenix)🔴🔴🟢🟢🟢🟢🔴🔴🔴🔴🔴⚪️
TRMNL (Ruby + Sinatra)🟡🔴🟡🟢🔴🔴🔴🔴🟢🟢⚫️🟢
Community (Python + Django)🔴🔴🟢🟢🟢🔴🔴🔴🟢🔴🟢⚪️
Community (PHP + Laravel)🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🔴
Community (Next.js)🟢🟢🟢🟢🟢🔴🔴🟢🔴🔴🟢⚪️
+ +The following provides a detailed breakdown of each of the above features: + +* **Dashboard**: Provides a high level overview of information mostly in terms of quick links, statistics, charts, graphs, system health, etc. +* **Auto-Provisioning**: Devices can be automatically provisioned once added to your network. This includes the automatic provisioning of new and existing devices. +* **Devices**: Provides device management in terms of updating each device, viewing current image, viewing logs, and more. +* **JSON Data API**: Provides full API support using a JSON Data API for device management, image generation, logging, and more. +* **Image Previews**: Provides a UI for quickly, and dynamically, generating new device screens. +* **Playlists**: Supports playlist configuration and management in terms of timing, order, and display of screens on devices. This can also include proxying to our Core server. +* **Plugins**: Supports installation and hosting of custom plugins. This can also include proxying to our Core server. +* **Recipes**: Supports installation and hosting of custom plugins. This can also include proxying to our Core server. +* **Docker**: Supports Docker for both local development and production deployment. +* **Test Suite**: Has a test suite with near 100% test coverage, is fully runnable locally, and is wired up with automatic Continuous Integration (CI) builds. +* **Maintained**: Project is maintained and kept up-to-date on a weekly (or monthly) basis in terms of dependencies, firmware updates, and keeping up-to-date with any/all Core changes. +* **Semantic Versioning**: Supports [strict semantic versioning](https://alchemists.io/articles/strict_semantic_versioning). + +### API + +At a minimum, the following API endpoints should be supported for all BYOS implementations: + +#### Display + +```bash +curl "http://byos.local/api/display" \ + -H 'ID: ' \ + -H 'Access-Token: ' \ + -H 'Accept: application/json' \ + -H 'Content-Type: application/json' +``` + +#### Images + +```bash +curl -X "POST" "http://byos.local/api/images" \ + -H 'ID: ' \ + -H 'Access-Token: ' \ + -H 'Accept: application/json' \ + -H 'Content-Type: application/json' \ + -d $'{ + "image": { + "content": "

Demo

" + "file_name": "demo" + } +}' +``` + +#### Logs + +```bash +curl "http://byos.local/api/log" \ + -H 'ID: ' \ + -H 'Access-Token: ' \ + -H 'Accept: application/json' \ + -H 'Content-Type: application/json' +``` + +#### Setup + +``` +curl "https://byos.local/api/setup/" \ + -H 'ID: ' \ + -H 'Access-Token: ' \ + -H 'Accept: application/json' \ + -H 'Content-Type: application/json' + +``` + +💡 For a detailed breakdown of all API endpoints and what they can do, please refer to the [Terminus API Documentation](https://github.com/usetrmnl/byos_hanami?tab=readme-ov-file#apis). + diff --git a/diy/introduction.md b/diy/introduction.md new file mode 100644 index 0000000..88d974c --- /dev/null +++ b/diy/introduction.md @@ -0,0 +1,35 @@ +--- +description: An introduction to running TRMNL on your own hardware. +--- + +# Introduction + +There are 4 flavors to building the perfect setup for your needs: + +1. **Default** - buy our device, that runs our firmware, and pings our server +2. **BYOD** - build your own device, that runs our firmware, and pings our server +3. **BYOD/S** - build your own device, mod our firmware, and ping your own server +4. **BYOS** - buy our device, mod our firmware, and ping your own server (_recommended_) + +### Choosing your stack + +After starting TRMNL and going down the rabbit hole of DIY smart home, IoT, e-ink, and gadget communities, it became clear that end-to-end ownership, security, and data privacy are critical ingredients to building trust. + +With this in mind we decided to [open source our firmware](https://github.com/usetrmnl/firmware) and provide [free guides](https://www.youtube.com/watch?v=3xehPW-PCOM) to reproduce the TRMNL experience _without_ our servers in the middle. + +When considering how to build your own e-ink dashboard, it is our opinion that... + +* If you're not comfortable with coding, Option #1 is ideal. +* If you're comfortable with high-level programming languages, Option #4 provides an 80/20 approach to privacy + security without breaking the bank or spending hours coding. +* If you have experience in C/C++, or have experience with micro controllers and Python, Option #2 will give you the pride of full control over the look and feel of your peripheral device. +* If you are a l33t programmer or simply have access to AI (half joking), Option #3 is the most comprehensive offering to customize TRMNL however you'd like. + +### Prerequisites + +Options 1, 3, and 4 are available to all customers for no extra charge. + +Option 2 requires a small monthly fee to cover your compute time on our servers, since we don't make any revenue on a device sale. + +### Next steps + +Once you've determined the best setup for your needs, find the relevant guide on the left underneath "DIY TRMNL." diff --git a/how-it-works.md b/how-it-works.md new file mode 100644 index 0000000..89b38b1 --- /dev/null +++ b/how-it-works.md @@ -0,0 +1,37 @@ +--- +description: Overview of the TRMNL architecture. +--- + +# How it Works + +

Device, Server, Native Plugins, 3rd Party Developer, Firmware Components

+ +The TRMNL **web server** hosts a growing directory of [native plugins](https://usetrmnl.com/integrations) and API endpoints + templating engine for **custom plugins**, managed directly by customers. Learn to build a custom plugin [here](https://help.usetrmnl.com/en/articles/9510536-custom-plugins). + +The TRMNL **device** is a custom PCB featuring an ESP32-C3 microcontroller, 1800-2500 mAh battery, and 7.5" EPD screen housed in injection-molded ABS soft touch plastic. Customers may disassemble their device, mod their firmware, and retrieve their API keys (_or pay a small one-time fee to unlock the Developer add-on_) without impacting our [Terms of Service](https://usetrmnl.com/terms). + +TRMNL **firmware** supports automatic OTA (over the air) updates to WiFi-connected devices and is [open source](https://github.com/usetrmnl/firmware). Here's how it works: + +1. TRMNL device wakes up and sends request to web server for displayable content every _n_ period\* +2. TRMNL web server generates a single-use, 1-bit, bitmap image (800x480 pixels). Response JSON includes a link to this image and timing instructions for the next "refresh" request. +3. TRMNL device renders the content, then goes to sleep for the instructed amount of time. + +{% hint style="info" %} +\* "Displayable content" is the most recently created screen, in order of priority according to the [Playlists](https://usetrmnl.com/playlists) interface. "N" is a value in minutes, configurable by customers from the [Devices > Edit](https://usetrmnl.com/devices/) web interface. +{% endhint %} + +## Opinionated device <> server relationship + +Most IoT products support SSH-ing directly into peripheral devices. We've heard too many horror stories about how this can go wrong, and decided to invert the paradigm. + +**Your TRMNL device pings our server, never the other way around**. + +Each request made to our `/api/display` endpoint includes only the minimum details needed to support customers -- an API key, device mac address, firmware version, battery voltage, and wifi signal strength. + +**We do not collect any footprint of your location or identity**, such as IP address or WiFi configuration. The SSID and password for your local network are stored only on your TRMNL device. + +When the TRMNL web server responds to a device's request we include only a few fields. These include "update\_firmware" (true/false), a direct download link to the firmware's \[public] binary package, and whether the device should be reset. Customers may reset their device to transfer ownership or safely destroy data from their web account. + +**TRMNL does not store rendered content over time.** + +Whenever the web server generates a new bitmap image, it replaces the previous image. This keeps our costs low, allowing us to provide perpetual service without subscription fees. This also protects users, because we only have access to the most recent screen rendered for each of your plugins. diff --git a/partners-api/getting-started.md b/partners-api/getting-started.md new file mode 100644 index 0000000..46d8e3a --- /dev/null +++ b/partners-api/getting-started.md @@ -0,0 +1,17 @@ +--- +description: Become a TRMNL Partner. +--- + +# Getting Started + +The TRMNL team manually approves each Partner, which involves the following: + +1. basic KYC to ensure our intentions align (_customer delight, not bulk discounts_) +2. program negotiation (net-30 vs on-demand payment terms, optional quotas, etc) +3. testing the Partner's custom plugin end-to-end + +**KYC** includes determining a Partner point of contact, getting some sense of weekly / monthly device provision volume, and possibly a small deposit. + +**Program negotiation** is simple and might look like this: "_Partner wants to award their customers a TRMNL device for 50% off. Partner can generate TRMNL discount codes for 50% off and will pay TRMNL monthly via invoice for the remaining 50%, less a 10% bulk discount._" + +**Testing** entails the Partner providing TRMNL a demo user account on the Partner platform, with instructions to set up their custom plugin manually on an existing TRMNL account. diff --git a/partners-api/introduction.md b/partners-api/introduction.md new file mode 100644 index 0000000..2f2b851 --- /dev/null +++ b/partners-api/introduction.md @@ -0,0 +1,15 @@ +--- +description: Provision devices and pre-load plugins for your customers. +--- + +# Introduction + +If you're a company with a custom plugin, TRMNL makes it easy to reward customers with free or discounted devices that arrive pre-connected to your platform. + +Example scenario: + +1. Acme SaaS has a subscriber dashboard for their email newsletter tool. Acme builds a custom plugin inside TRMNL to showcase this information. +2. Acme wants to gift a free TRMNL device to their top customers. Acme creates coupon codes with the TRMNL Partners API that their customer can use at checkout for X% off their order. +3. When Acme's customer device is shipped, it is associated to their account on Acme's platform. Upon unboxing + WiFi pairing, Acme's customer will see their Acme native dashboard on TRMNL device without any additional setup. + +Continue reading to learn how this works with just a single API request, or email [partners@usetrmnl.com](mailto:partners@usetrmnl.com) to get started. diff --git a/partners-api/provisioning-devices.md b/partners-api/provisioning-devices.md new file mode 100644 index 0000000..b264f67 --- /dev/null +++ b/partners-api/provisioning-devices.md @@ -0,0 +1,48 @@ +--- +description: Stub a device + discount code with the Partners API. +--- + +# Provisioning Devices + +To preload a custom plugin on a device for your customer, you can generate a coupon code and share the customer's relevant credentials in a single API request. + +**Step 1 - Partner requests a coupon** + +``` +POST /api/partners + +Headers: +Client-ID, Access-Token # provided by TRMNL team + +Body: +{ + partner: { + action: "provision_discount", + data: { "user-data": "goes here", "more-data": "also ok" }, + meta: { "expires_at": "2025-03-20" } # will expire at 23:59 EST on this date + } +} +``` + +Based on your program terms, TRMNL will generate a coupon with your Partner name + unique suffix. + +``` +# response example +{ status: 200, data: { code: "acme-123456789" } } +``` + +If a quota is preferred, for example 50x maximum provisions per month, this endpoint will return a `nil`code value when the quota is breached. + +**Step 2 - TRMNL generates a coupon** + +Provide the `code`from Step 1 to your customer with instructions to purchase a device from usetrmnl.com. They can provide this code at checkout. + +If your discount is for 100% off, they will not be charged. If your code is for 50% off, they will pay 50% at checkout. You will be billed via invoice later for claimed discount codes during the agreed period. Additional terms are possible, for example requiring customers to pay for shipping, or only subsidizing a device with our regular (vs large size) battery, etc. + +**Step 3 - TRMNL pre-loads the Partner's plugin** + +Prior to this workflow being implemented, TRMNL should have already tested your custom plugin. Assuming it requires some kind of API credential to be accessed by a TRMNL user's device, the Step 1 payload should include these details inside the `data`node. + +Whatever key/values are provided in the `data` node of Step 1 will be saved to the user's pre-loaded plugin when their device is unboxed and set up. Thus these key/values should match exactly the merge variables required by the plugin setting instance. + +Contact [partners@usetrmnl.com](mailto:partners@usetrmnl.com) with questions or requests. diff --git a/plugin-marketplace/going-live.md b/plugin-marketplace/going-live.md new file mode 100644 index 0000000..37b1303 --- /dev/null +++ b/plugin-marketplace/going-live.md @@ -0,0 +1,43 @@ +--- +description: Publish your plugin for all users with a simple submission flow. +--- + +# Going Live + +After building and testing your plugin, copy/paste the following application into an email to team@usetrmnl.com. + +Subject: + +``` +Public plugin submission - {{ plugin name }} +``` + +Body: + +``` +Hi team, + +Please review my plugin for the public marketplace: + +Plugin ID: {{ visit My Plugins > click Edit, copy integer ID from URL }} +Owner Email: {{ should == inbox from which you are sending this message }} + +Why should this plugin be public vs private? How does it help other users? +{{ should be different than your Description field. if your plugin is against our ethos (breeds distraction, not focus), it may be better as an open source + Private plugin. }} + +Video demonstration: +{{ link to video, no audio required, of the plugin being installed from scratch. }} + +How can we test this plugin works? +{{ preferably a demo login email/password that we can own forever, ex "team@usetrmnl.com" }} + +Will you promote TRMNL when this plugin is published? If so, how/where? +{{ no wrong answers, but we prioritize plugins that help us grow }} + +Thanks, +{{ your name }} +``` + +After receiving this information we'll test out your plugin, send revision requests (if applicable), and soft launch it to our Plugins marketplace. + +We can then discuss featuring your work in an upcoming email newsletter or other social channels. At any time you are welcome to post it in our developer-only Discord's "#flex" channel, prior to public approval. diff --git a/plugin-marketplace/introduction.md b/plugin-marketplace/introduction.md new file mode 100644 index 0000000..fe517fe --- /dev/null +++ b/plugin-marketplace/introduction.md @@ -0,0 +1,15 @@ +--- +description: >- + TRMNL's public marketplace lets any user publish or use another customer's + integration. +--- + +# Introduction + +Our plugin marketplace allows developers to create their own public plugins for other users to install. In the future, the plugin marketplace will enable developers to earn money per install, similar to the Shopify App Marketplace or Google Play Store. + +There are 2 approaches to publishing a plugin for other users — Public, or Recipe. Review the differences between them before continuing: + +{% embed url="https://help.usetrmnl.com/en/articles/10546870-compare-custom-plugin-types" %} + +**Recipes** may be built inside the TRMNL interface, and do not require any 3rd party dependencies, user auth, etc. Whereas **Public plugins** require the OAuth2 flow. In this scenario, the plugin author (you) maintain user data and are responsible for data privacy and security. TRMNL will fetch markup content at regular intervals and generate an image for the connected user's device to display. diff --git a/plugin-marketplace/plugin-creation.md b/plugin-marketplace/plugin-creation.md new file mode 100644 index 0000000..441977d --- /dev/null +++ b/plugin-marketplace/plugin-creation.md @@ -0,0 +1,31 @@ +--- +description: Creating a plugin OAuth client. +--- + +# Plugin Creation + +You can create a new plugin by visiting the following URL: + +``` +https://usetrmnl.com/plugins/my/new +``` + +

TRMNL public plugin client

+ +You'll have to provide the following information about your plugin. + +**Name**: Branded title (if applicable, ex "Vandelay Industries") or brief tag that describes the plugin's functionality + +**Description**: Additional text to help differentiate your plugin from others + +**Icon**: PNG format preferred + +**Installation URL**: Endpoint where TRMNL should trigger the installation flow + +**Installation Succes Webhook URL**: Where you want to receive installation success events as a webhook + +**Plugin Management URL**: Where TRMNL users can manage their plugins + +**Plugin Markup URL:** Endpoint where TRMNL should ping your webserver for markup content + +**Uninstallation Webhook URL**: Where you want to receive uninstallation events as a webhook diff --git a/plugin-marketplace/plugin-installation-flow.md b/plugin-marketplace/plugin-installation-flow.md new file mode 100644 index 0000000..22bbb6d --- /dev/null +++ b/plugin-marketplace/plugin-installation-flow.md @@ -0,0 +1,70 @@ +--- +description: OAuth installation flow between TRMNL and your web server. +--- + +# Plugin Installation Flow + + + +
+ +1. **Installation Request** + +When the user installs your plugin, TRMNL sends an installation request to `installation_url` with unique `token` and `installation_callback_url`. + +2. **Fetch Access Token** + +After receiving the request, Your server using the `client_id`, `client_secret` and `token` from step#1 request the `access_token` from TRMNL using the following endpoint: + +``` +body = { + code: 'code-from-step-1', + client_id: 'your-plugin-client-id', + client_secret: 'your-plugin-secret', + grant_type: 'authorization_code' +} +response = HTTParty.post("https://usetrmnl.com/oauth/token", body: body) +response['access_token'] +``` + +3. **Access Token** + +TRMNL responds with the `access_token`. + +4. **Installation Callback** + +Use the `installation_callback_url` from Step #1 and redirect the user back to TRMNL. + +5. **Success Webhook** + +After the user has successfully finished installing the plugin, TRMNL sends a success notification to `installation_success_webhook_url` endpoint. Data is sent in JSON format as follows. + +HTTP Headers: + +``` +{ 'Authorization': 'Bearer ', 'Content-Type': 'application/json' } +``` + +Body: + +``` +{ + "user": { + "name":"Ronak J", + "email":"ronak@usetrmnl.com", + "first_name":"Ronak", + "last_name":"J", + "locale":"en", + "time_zone":"Pacific Time (US & Canada)", + "time_zone_iana":"America/Los_Angeles", + "utc_offset":-28800, + "plugin_setting_id":1234, + "uuid": "674c9d99-cea1-4e52-9025-9efbe0e30901" + } +} +``` + +Time zone mappings are available here under "Constants:"\ +[https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html](https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html) + +The `plugin_setting_id`is useful for building a redirect URI in your own application, for example to send a user back to usetrmnl.com/plugin\_settings/:plugin\_setting\_id/edit. diff --git a/plugin-marketplace/plugin-management-flow.md b/plugin-marketplace/plugin-management-flow.md new file mode 100644 index 0000000..b2b4bc0 --- /dev/null +++ b/plugin-marketplace/plugin-management-flow.md @@ -0,0 +1,20 @@ +--- +description: Ability for users to manage their plugin on your weber server. +--- + +# Plugin Management Flow + +
+ +After a user installs your plugin, they may want to manage the plugin settings. TRMNL will redirect the user to the `plugin_management_url` with a unique user identifier (UUID) params, so that you can identify them on your web server. + +Example request:\ +`https://yourapp.com/manage?uuid=ae48d6ac-48f4-4aed-8464-bad68368e97c` + +**Note**: The UUID is the unique user identifier in the TRMNL plugin architecture. This allows TRMNL users to have multiple instances of the same plugin, each with their own settings. + +If you saved the `plugin_setting_id` from the [Installation Flow](plugin-installation-flow.md), you can build a helpful "Back to TRMNL" button in your Management UI. + +By appending `?force_refresh=true` to your return link, TRMNL will invoke a [Screen Generation request](plugin-screen-generation-flow.md) on the user's behalf and present a toast message when they're back inside the TRMNL application. + +

Force Refresh toast message

diff --git a/plugin-marketplace/plugin-screen-generation-flow.md b/plugin-marketplace/plugin-screen-generation-flow.md new file mode 100644 index 0000000..810b3d6 --- /dev/null +++ b/plugin-marketplace/plugin-screen-generation-flow.md @@ -0,0 +1,35 @@ +--- +description: Creating image content to display on a user's device. +--- + +# Plugin Screen Generation Flow + +
+ +TRMNL generates a screen every X minutes, where X is the refresh frequency set by the user. + +TRMNL generates screens by sending a POST request to the `plugin_markup_url` endpoint you specified during [Plugin Creation](plugin-creation.md). The request body will include the `user_uuid` (that particular user's plugin connection UUID). The request header contains an `authorization` key with the user's plugin connection `access_token`as the Bearer token. Here's an example of our server request: + +```bash +curl -XPOST 'https://your-server.com/your-markup-url' \ +-H 'Authorization: Bearer xxx' \ +-d 'user_uuid=xx' +``` + +Your web server should respond with HTML inside root nodes named `markup`, `markup_quadrant`, and so on to satisfy each layout offered by TRMNL. This markup should include whatever values you want the user to see rendered on their screen. + +{% hint style="success" %} +**Pro tip**: use the [Private Plugin](https://usetrmnl.com/plugin_settings/new?keyname=private_plugin) markup editor to develop the frontend of your plugin. This in-browser text editor supports live refresh and automatically applies the correct styling and JavaScript helpers to your markup. +{% endhint %} + +TRMNL uses the markup in your server's response to generate an e-ink friendly image. If the user connecting your plugin created a "full screen" playlist item, TRMNL will leverage the HTML inside the `markup` node. If they connected your plugin as part of a left/right Mashup, TRMNL will look for HTML inside the `markup_half_vertical` node. + +Here's an example of a valid server response: + +```json +{ + "markup": '
Daily Scripture
Hello
World
', "markup_half_horizontal": '
Your content
', "markup_half_vertical": '
Your content
', "markup_quadrant": '
Your content
' +} +``` + +**Note:** in order for your plugin to be published in the TRMNL public marketplace, you must provide HTML for all available markup layouts. [View them here](https://help.usetrmnl.com/en/articles/10168132-mashups). diff --git a/plugin-marketplace/plugin-uninstallation-flow.md b/plugin-marketplace/plugin-uninstallation-flow.md new file mode 100644 index 0000000..5a9daf6 --- /dev/null +++ b/plugin-marketplace/plugin-uninstallation-flow.md @@ -0,0 +1,25 @@ +--- +description: Handling user uninstallation requests on your web server. +--- + +# Plugin Uninstallation Flow + +
+ +When a user uninstalls your plugin, as a best practice TRMNL will send a notification via webhook. The request is sent to the `uninstallation_webhook_url` in JSON format with the following details: + +HTTP Headers: + +``` +{ 'Authorization': 'Bearer ', 'Content-Type': 'application/json' +``` + +Body: + +``` +{ + "user_uuid": "uuid-of-the-user" +} +``` + +Parse this webhook payload to perform a "teardown" or similar strategy on your web server. diff --git a/private-api/fetch-plugin-content.md b/private-api/fetch-plugin-content.md new file mode 100644 index 0000000..1ceeb1a --- /dev/null +++ b/private-api/fetch-plugin-content.md @@ -0,0 +1,43 @@ +--- +description: Retrieve parsed plugin JSON data for your own templates. +--- + +# Fetch Plugin Content + +No matter how many customizations we add to native plugins, there will always be a good reason to change them. Instead of cluttering our interface and adding complexity for other users, TRMNL offers a "data only" mode for select native plugins. + +{% hint style="info" %} +For more context on this feature, go [here](https://usetrmnl.com/blog/calendar-hackathon). +{% endhint %} + +### How it works + +First, set up + hide an instance of the plugin you want to re-build yourself with raw data. This instructs TRMNL to sync and parse data on your behalf. + +1. Connect the Weather, Stock Prices, or any Calendar plugin (more coming soon) +2. Make note of the PluginSetting integer ID in the URL (`/plugin_settings/`) +3. Navigate to Playlists and "hide" the automatically added item by clicking the eyeball icon + +Next, build a Private Plugin. + +1. Navigate to Plugins > Private Plugin, select "Polling" as the Strategy +2. Input `https://usetrmnl.com/api/plugin_settings//data` as the Polling URL +3. Input `authorization=bearer ` as the Polling Header\* + +\*Find or generate a [User API Key](https://help.usetrmnl.com/en/articles/11195228-user-level-api-keys) on your Account tab + +Click save, then enter the Markup Editor. + +

Private Plugin > Edit Markup

+ +Parsed data will appear inside a `data` node of the "Merge Variables" dropdown. You may need to click "Force Refresh" from the private plugin settings view to ensure the data has been fetched. + +

Example - Google Calendar "data mode"

+ +### Markup Quickstart + +If you only want to make small changes to the TRMNL native design, you can steal that markup here: + +[https://usetrmnl.com/plugins/demo](https://usetrmnl.com/plugins/demo) (requires login) + +Just click the plugin you're rebuilding, and all layouts will appear with sample data embedded. If you've connected a plugin natively, your latest cached JSON will be embedded instead of demo data. diff --git a/private-api/fetch-screen-content.md b/private-api/fetch-screen-content.md new file mode 100644 index 0000000..864172d --- /dev/null +++ b/private-api/fetch-screen-content.md @@ -0,0 +1,62 @@ +--- +description: Retrieve TRMNL image data, device-free. +--- + +# Fetch Screen Content + +First, set up a TRMNL device or [BYOD license](https://shop.usetrmnl.com/products/byod). + +Next, grab your API Key from [Devices > Edit](https://usetrmnl.com/devices) and make a request like below. + +### Auto advance content + +This endpoint is used by our firmware (on your device) to fetch new screen content. Making a request to this endpoint automatically 'advances' your Playlist to the next item in your queue. To simply grab the current screen instead, skip to the next section below. + +``` +curl https://usetrmnl.com/api/display --header "access-token:xxxxxx" +``` + +This will respond with several fields, for example: + +``` +{ + "status"=>0, # will be 202 if no user_id is attached to device + "image_url"=>"https://trmnl.s3.us-east-2.amazonaws.com/path-to-img.png", + "image_name"=>"plugin-YYYY-MM-DD-TXX-XX-XXZ-hash", + "update_firmware"=>false, + "firmware_url"=>nil, + "refresh_rate"=>"1800", + "reset_firmware"=>false +} +``` + +The `image_url` is likely the most interesting to you, as this may be leveraged by your own hardware to render content however you see fit. + +{% hint style="info" %} +**Note**: TRMNL devices send a few additional values in the request headers by default, such as your WiFi connection strength (RSSI value), firmware version (ex: 1.3.7), and more. + +These attributes impact the response content by instructing the device to either update firmware, change its refresh rate, and so on. But excluding these values from your request is OK, just be aware that some response values may be nil. +{% endhint %} + +### Current screen + +If you're expanding a TRMNL fleet with BYOD devices, such as a [Raspberry Pi](https://usetrmnl.com/blog/rpi-trmnl) or [Kindle](https://usetrmnl.com/guides/turn-your-amazon-kindle-into-a-trmnl), [Android](https://github.com/usetrmnl/trmnl-android), or [Kobo](https://github.com/usetrmnl/trmnl-kobo) tablet, you may prefer to mirror whatever content is showing on your official TRMNL or BYOD device. + +``` +curl https://usetrmnl.com/api/current_screen --header "access-token:xxxxxx" +``` + +This will respond with the following fields: + +``` +{"status" => 200, + "refresh_rate" => 1800, + "image_url" => "https://usetrmnl.com/rails/active_storage/blobs/redirect/hash-here/plugin-YYYY-MM-DD-TXX-XX-XXZ-hash", + "filename" => "plugin-YYYY-MM-DD-TXX-XX-XXZ-hash", + "rendered_at" => nil + } +``` + +{% hint style="info" %} +**Note**: the `current_screen` endpoint was designed for consumption by our [Chrome extension](https://usetrmnl.com/chrome). Please don't abuse it. +{% endhint %} diff --git a/private-api/introduction.md b/private-api/introduction.md new file mode 100644 index 0000000..c73510f --- /dev/null +++ b/private-api/introduction.md @@ -0,0 +1,35 @@ +--- +description: Advanced features available for Developer edition devices. +--- + +# Introduction + +As outlined in our [open source firmware](https://github.com/usetrmnl/firmware), TRMNL exposes a GET endpoint that responds with image and other content for your device to store or render. + +``` +GET /api/display + +# request headers example +{ + 'ID' => 'XX:XX:XX:XX', + 'Access-Token' => '2r--SahjsAKCFksVcped2Q' +} + +# response body example +{ + "image_url"=>"https://trmnl.s3.us-east-2.amazonaws.com/path-to-img.bmp", + "image_name"=>"2024-09-20T00:00:00", + "update_firmware"=>false, +} +``` + +The TRMNL server leverages your device's immutable Mac Address (`ID` ) header during initial setup, then depends on the `Access-Token`header for subsequent requests. + +Thus, **if you know your device's API key, you can request content without a TRMNL device or TRMNL firmware**. + +In these \[WIP] Private API docs we'll outline a few ways to take advantage of this information for your own privacy, security, and experimentation purposes. + +{% hint style="info" %} +**Only devices with the Developer add-on** may access their own Access Token. You may unlock this feature anytime from your [Devices > Edit](https://usetrmnl.com/devices/) page for a one-time fee. +{% endhint %} + diff --git a/private-plugins/create-a-screen.md b/private-plugins/create-a-screen.md new file mode 100644 index 0000000..7ef12b2 --- /dev/null +++ b/private-plugins/create-a-screen.md @@ -0,0 +1,50 @@ +--- +description: >- + Leverage our simple RESTful endpoints to generate custom screens on your TRMNL + device. +--- + +# Create a screen + +{% hint style="info" %} +### Before you begin + +Creating screens requires a Private Plugin instance inside your TRMNL account. This is currently available via the web interface only. Simply navigate to [Plugins > Private Plugin > New](https://usetrmnl.com/plugin_settings/new?keyname=private_plugin). + +**Only devices with the Developer add-on** may access their own Access Token. You may unlock this feature anytime from your [Devices > Edit](https://usetrmnl.com/devices/) page for a one-time fee.e a screen (webhook strategy) +{% endhint %} + +If your private plugin's "Strategy" is set to Webhook, you can provide data to TRMNL's server at any time. + +Simply send a `POST` request to the instance's Webhook URL, accessible from the configuration form. The below example updates the Plugin with UUID "asdfqwerty1234" using dynamic values inside a "merge\_variables" node: + +``` +curl "https://usetrmnl.com/api/custom_plugins/asdfqwerty1234" \ + -H "Content-Type: application/json" \ + -d '{"merge_variables": {"text":"You can do it!", "author": "Rob Schneider"}}' \ + -X POST +``` + +The Plugin's UUID value (`asdfqwerty1234` in the example above) will associate your payload with the Markup you already provided when you [created your private plugin](https://help.usetrmnl.com/en/articles/9510536-custom-plugins). + +To retrieve your Plugin's Webook URL / UUID, visit your private plugin instance. Note that you must "save" (create) the private plugin before a UUID and Webhook URL will be generated. + +

Private Plugin Webhook URL w/ UUID

+ +## Create a screen (polling strategy) + +If your private plugin's "Strategy" is set to Polling, the TRMNL server will periodically fetch for new data from an endpoint of your choice. + +Simply provide a "Polling URL" inside your private webhook instance (web UI), and TRMNL will make a `GET` request to that URL. To test this quickly, we've prepared an endpoint that responds with `text` and `author` key/value pairs, along with a `collection` array for quick demonstration: + +[https://usetrmnl.com/custom\_plugin\_example\_data.json](https://usetrmnl.com/custom_plugin_example_data.json) + +If your desired polling URL contains a collection/array in the root node, TRMNL will nest it inside a key named "data" for accessibility by the Liquid templating engine. Here is an example of that style payload: + +[https://usetrmnl.com/custom\_plugin\_example\_data.json?collection\_only=true](https://usetrmnl.com/custom_plugin_example_data.json?collection_only=true) + +**Note**: With the Polling strategy, variables _do not_ need to be nested within a "merge\_variables" node. That is only a requirement for the Webhook strategy. + +## Troubleshooting + +For more assistance, see our [Private Plugin Tutorial](https://help.usetrmnl.com/en/articles/9510536-custom-plugins) or email team@usetrmnl.com. diff --git a/private-plugins/templates-1.md b/private-plugins/templates-1.md new file mode 100644 index 0000000..77ef8bc --- /dev/null +++ b/private-plugins/templates-1.md @@ -0,0 +1,326 @@ +--- +description: TRMNL's native design system for developing beautiful, e-ink friendly screens. +hidden: true +--- + +# 🖼️ Plugin Templating (DEPRECATED, hidden) + +## Overview + +The TRMNL device is an **800x480 pixel, black and white, 1-bit grayscale display**. This meant we had to abandon a lot of modern web styling techniques when developing the API. For example: no gradients, no fancy typeface, and no anti-aliasing. + +We replaced each of these strategies with dithering patterns, bitmap fonts, and a TRMNL-flavored design system. In these docs you'll learn how to adopt our system to build your own beautiful plugins. + +## Quickstart + +Create an HTML file with our plugins CSS embedded in the ``. + +```erb + + + + + + +
+
+
+
+
+
+ Motivational Quote +
“I love inside jokes. I hope to be a part of one someday.”
+ Michael Scott +
+
+
+
+ +
+ + Plugin Title + Instance Title +
+
+
+ + +``` + +The above markup should produce a screen like this: + +

Sample screen render with TRMNL's plugin CSS stylesheet

+ +After designing a screen to your liking (more examples below), replace dynamic content with `{{ variable }}` references. TRMNL uses the [Liquid templating library](https://shopify.github.io/liquid/) by Shopify to interpolate values into your template markup. + +Next, extract inner the `
` content and paste it into the Markup field of a [custom plugin](https://help.usetrmnl.com/en/articles/9510536-custom-plugins) inside your TRMNL account. + +{% hint style="info" %} +[Tutorial - How to create a custom plugin](https://help.usetrmnl.com/en/articles/9510536-custom-plugins) +{% endhint %} + +**Note**: You may also leverage [Liquid Filters](https://shopify.dev/docs/api/liquid/filters) to reduce the sanitization required by the service producing data for your TRMNL plugins. For example, you can convert "10" to "$10.00" via [money\_with\_currency](https://shopify.dev/docs/api/liquid/filters/money). + +## Basic Structure + +```erb +
+
+
+ {{ content }} +
+
+
+ +
+ + {{ plugin_name }} + {{ instance_name }} +
+``` + +## Columns + +Place content inside a single or multiple `column` class divs. + +This ensures that the content is centered vertically on the screen, and aligned to the top of the column with with most content. + +```erb +
+
+
+ {{ content }} +
+
+
+... +``` + +## Gray Scale + +Use `r-bg-{{ shade }}` classes to define background patterns from the gray scale. + +

Grayscale shades

+ +
ShadeClass
Blackr-bg-black
Gray 1r-bg-gray-1
Gray 2r-bg-gray-2
Gray 3r-bg-gray-3
Gray 4r-bg-gray-4
Gray 5r-bg-gray-5
Gray 6r-bg-gray-6
Gray 7r-bg-gray-7
Whiter-bg-white
+ +## Components + +### Title & Description + +
+ +```ruby +Title +Title Small +Description +``` + +### Label + +
+ +```ruby +Label +Label Underline +Label Small +Label Small Underline +``` + +### Value + +
+ +```ruby +24,276 +24,276 +24,276 +24,276 +24,276 +24,276 +``` + +### Grid + +Here's a simple grid markup of an equal three column grid, with gaps between the columns: + +``` +
+
...
+
...
+
...
+
+``` + +### Item + +

Example of List components

+ +```erb +
+
+ 1 +
+
+ Monthly Catchup with Dev Team + A monthly meeting to discuss progress and obstacles with the development team +
+ 10:00 - 11:00 + Confirmed +
+
+
+``` + +### Table + +
+ +```ruby + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MetricValue
Recipients20,129
Open rate6.55%
Click rate0.76%
Unsubscribes14
Clicks654
StatusCompleted
+``` + +### Fully built Plugin + +
+ +``` +
+
+
+
+
+
+ 120,826 + Views +
+
+
+
+
+
+
+ 180+ + Days Watched +
+
+
+
+
+
+
+
+
+ + 2m 9s + + Average View Duration +
+
+
+
+
+ 789 + Subscribers Gained +
+
+
+
+
+ 1% + View Through Rate +
+
+
+
+
+
+
+
+ 18,567 + Likes +
+
+
+
+
+ 4,568 + Comments +
+
+
+
+
+ 2,987 + Shares +
+
+
+
+ +
+ + YouTube + @MrBeast +
+``` + +### Custom Graphs + +
+ +```css +#github_commit_graph { + width: 758px; + height: 148px; + overflow: hidden; + + column-count: auto; + column-fill: auto; + column-width: 13px; + column-gap: 0px; +} + +#github_commit_graph .day { + width: 11px; + height: 19px; + float: left; + border-radius: 4px; + margin: 2px 2px 0 2px; + + break-inside: avoid-column; +} +``` diff --git a/private-plugins/templates-advanced.md b/private-plugins/templates-advanced.md new file mode 100644 index 0000000..ea8a466 --- /dev/null +++ b/private-plugins/templates-advanced.md @@ -0,0 +1,68 @@ +--- +description: Go deeper with custom screen styling, data visualization, and more. +--- + +# Screen Templating (Graphics) + +## Overview + +The TRMNL design system is actively improving to suit the needs of our [growing plugin directory](https://usetrmnl.com/integrations) and requests from developers like you. + +As we extend [native components](https://usetrmnl.com/framework), you are welcome to provide in-line styling to plugin markup to achieve your desired effect. + +You may also included 3rd party libraries, for example [Highcharts](https://www.highcharts.com/), to create data visualizations like charts and graphs. + +## Quickstart + +Here's some example Markup content that will render an _ugly_ line chart: + +``` + +
+ +
+
+
+
+ +``` + +If this is saved into a [Private Plugin](https://usetrmnl.com/plugin_settings?keyname=private_plugin) > Markup field, the following screen will be rendered: + +

Un-styled chart example

+ +As you can see, this isn't pretty (yet). + +Here's another line chart with TRMNL-friendly styling: + +

Styled line chart example

+ +Get all the code + learn how to do this here:\ +[https://usetrmnl.com/framework/chart](https://usetrmnl.com/framework/chart) + +## More Charts and Graphs + +Our [Framework docs](https://usetrmnl.com/framework) are the best place for the latest examples and tips to improve the look and feel of graphical embeds from 3rd party tools like Highcharts. diff --git a/private-plugins/templates.md b/private-plugins/templates.md new file mode 100644 index 0000000..0b67fb9 --- /dev/null +++ b/private-plugins/templates.md @@ -0,0 +1,82 @@ +--- +description: TRMNL's native design system for developing beautiful, e-ink friendly screens. +--- + +# Screen Templating + +## Overview + +The TRMNL device is an **800x480 pixel, black and white, 1-bit grayscale display**. This means we had to abandon a lot of modern web styling techniques when developing the API. Learn more about this process [here](https://usetrmnl.com/blog/design-system). + +For the latest documentation on building beautiful plugins with TRMNL, see our Framework docs: + +[https://usetrmnl.com/framework](https://usetrmnl.com/framework) + +### Quickstart (TRMNL account) + +The easiest way to start building with TRMNL is by [making a Private Plugin](https://usetrmnl.com/plugin_settings?keyname=private_plugin) from inside your account. This includes an inline editor, merge variable interpolation, and a live previewer. + +

TRMNL markup editor with live preview

+ +### Quickstart (no TRMNL account) + +Create an HTML file with our plugins CSS + JS embedded in the ``. + +The example below has simple markup for a "full" layout plugin. We also offer half vertical, half horizontal, and quadrant sized layouts. + +```erb + + + + + + + +
+
+
+
+
+
+ Motivational Quote +
“I love inside jokes. I hope to be a part of one someday.”
+ Michael Scott +
+
+
+
+ +
+ + Plugin Title + Instance Title +
+
+
+ + +``` + +The above markup should produce a screen like this: + +

Sample screen render with TRMNL's plugin CSS stylesheet

+ +Note: in some cases you may need to include the 'Inter' font (inside the ``) to achieve the same look and feel as TRMNL's in-browser markup editor described above: + +``` + + + +``` + +### Customize and make it dynamic + +Use our [Framework Docs](https://usetrmnl.com/framework) to enhance your design and show/hide logic (example: [overflow management](https://usetrmnl.com/framework/overflow), [number formatting](https://usetrmnl.com/framework/format_value)). + +When you're satisfied with the design, replace dynamic content with `{{ variable }}` references. TRMNL uses the [Liquid templating library](https://shopify.github.io/liquid/) by Shopify to interpolate values into your template markup. You can then save + +{% hint style="info" %} +[Tutorial - How to create a custom plugin](https://help.usetrmnl.com/en/articles/9510536-custom-plugins) +{% endhint %} + +**Note**: You may also leverage [Liquid Filters](https://shopify.dev/docs/api/liquid/filters) to reduce the sanitization required by the service producing data for your TRMNL plugins. For example, you can convert "10" to "$10.00" via [money\_with\_currency](https://shopify.dev/docs/api/liquid/filters/money).