mirror of
https://github.com/usetrmnl/terminus.git
synced 2026-08-13 14:29:27 -07:00
0.1.0
Necessary to pick up the latest RuboCop changes and Dry Monads enhancements. Milestone: minor
:toc: macro
:toclevels: 5
:figure-caption!:
:docker_link: link:https://www.docker.com[Docker]
:hanami_link: link:https://hanamirb.org[Hanami]
:htmx_link: link:https://htmx.org[htmx]
:overmind_link: link:https://github.com/DarthSim/overmind[Overmind]
:puma_link: link:https://puma.io[Puma]
:ruby_link: link:https://www.ruby-lang.org[Ruby]
:sqlite_link: link:https://www.sqlite.org[SQLite]
:trmnl_link: link:https://usetrmnl.com[TRMNL]
:yjit_link: link:https://github.com/ruby/ruby/blob/master/doc/yjit/yjit.md[YJIT]
= Terminus
This is a {ruby_link}/{hanami_link} project that allows you to point a {trmnl_link} device to your own server which can be running on your local network or in the cloud. This is also the flagship implementation officially supported by {trmnl_link}.
toc::[]
== Features
* Allows you to run your own server.
* Built atop {ruby_link} and {hanami_link}.
* Uses {htmx_link}.
* Uses {puma_link}.
* Uses {sqlite_link}.
* Supports {yjit_link}.
* Supports {overmind_link}.
* Supports {docker_link}.
* Supports {trmnl_link} devices.
The following is a high level overview you can use to compare/contrast when deciding between using this Build Your Own Server (BYOS) implementation or our link:https://usetrmnl.com[hosted] solution.
*Legend*
* βͺοΈ Planned.
* π’ Supported.
* π‘ Partially supported.
* π΄ Not supported, not implemented, or isn't applicable.
*Matrix*
[options="header"]
|===
| | Terminus | Hosted
| Dashboard | π‘ | π’
| Plugins | βͺοΈ | π’
| Playlists | π‘ | π’
| Playlist Previews | βͺοΈ | π’
| Recipes | π΄ | π’
| Devices | π’ | π’
| Account | π΄ | π’
| JSON Data API | π’ | π’
| Open Source Components | π‘ | π‘
| Docker | π’ | π΄
|===
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. π
== Screencasts
video::3xehPW-PCOM[youtube,role=video]
== Requirements
. {ruby_link}.
. {hanami_link}.
. {overmind_link} (optional).
. {docker_link} (optional).
. A TRMNL device.
== Setup
To set up project, run:
[source,bash]
----
git clone https://github.com/usetrmnl/byos_hanami terminus
cd terminus
bin/setup
----
== Usage
To launch the server, run:
[source,bash]
----
bundle exec hanami assets compile
bundle exec puma --config ./config/puma.rb
----
π‘ Install {overmind_link} and run `overmind start` to run with full access to all processes (including remote debugging).
To view the app, use either of the following:
* *Secure*: https://localhost:2443
* *Insecure*: http://localhost:2300
=== APIs
The following APIs are supported. Each uses HTTPS which requires accepting your locally generated SSL certificate. If you don't want this behavior, you can switch to using HTTP (see above).
==== Display
Used for displaying new content to your device. Your device's refresh determines how often this occurs.
.Request
[%collapsible]
====
*Without Base64 Encryption*
[source,bash]
----
curl "https://localhost:2443/api/display/" \
-H 'ID: <redacted>' \
-H 'Access-Token: <redacted>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
----
*With Base64 Encryption via HTTP Header*
[source,bash]
----
curl "https://localhost:2443/api/display/" \
-H 'ID: <redacted>' \
-H 'Access-Token: <redacted>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'BASE64: true'
----
*With Base64 Encryption via Parameter*
[source,bash]
----
curl "https://localhost:2443/api/display/?base_64=true" \
-H 'ID: <redacted>' \
-H 'Access-Token: <redacted>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
----
====
.Response
[%collapsible]
====
*Without Base64 Encryption*
[source,json]
----
{
"filename": "demo.bmp",
"firmware_url": null,
"image_url": "https://localhost:2443/assets/images/generated/demo.bmp",
"refresh_rate": 130,
"reset_firmware": false,
"special_function": "sleep",
"status": 0,
"update_firmware": false
}
----
*With Base64 Encryption*
[source,json]
----
{
"filename": "demo.bmp",
"firmware_url": null,
"image_url": "data:image/bmp;base64,<truncated>",
"refresh_rate": 200,
"reset_firmware": false,
"special_function": "sleep",
"status": 0,
"update_firmware": false
}
----
====
==== Setup
Uses for new device setup and then never used after.
.Request
[%collapsible]
====
[source,bash]
----
curl "https://localhost:2443/api/setup/" \
-H 'ID: <redacted>' \
-H 'Access-Token: <redacted>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
----
====
.Response
[%collapsible]
====
[source,json]
----
{
"api_key": "<redacted>",
"friendly_id": "ABC123",
"image_url": "https://localhost:2443/images/setup/logo.bmp",
"message": "Welcome to TRMNL BYOS",
"status": 200
}
----
====
==== Logs
Uses for logging information about your server and/or device. Mostly used for debugging purposes.
.Request
[%collapsible]
====
[source,bash]
----
curl -X "POST" "https://localhost:2443/api/log" \
-H 'ID: <redacted>' \
-H 'Access-Token: <redacted>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
----
====
.Response
[%collapsible]
====
Logs details and answers a HTTP 204 status with no content.
====
==== Images
Used for generating new images by supplying HTML content for rendering, screenshotting, and greyscaling to render properly on your device.
.Request
[%collapsible]
====
[source,bash]
----
curl -X "POST" "https://localhost:2443/api/images" \
-H 'ID: <redacted>' \
-H 'Access-Token: <redacted>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d $'{
"image": {
"content": "<p>Test</p>"
"file_name": "test"
}
}'
----
====
.Response
[%collapsible]
====
[source,json]
----
{
"path": "$HOME/Engineering/terminus/public/assets/images/generated/test.bmp"
}
----
====
π‘ The images API supports full HTML so you can supply CSS styles, full DOM, etc. At a minimum, you'll want to use the following to prevent white borders showing up around your generated images:
[source,css]
----
* {
margin: 0;
}
----
If you don't supply a `file_name`, the server will generate one for you using a UUID for the file name. You can find all generated images in `public/images/generated`.
π‘ The `ID` is your device's MAC and the `Access-Token` is your device API Key.
== Development
To contribute, run:
[source,bash]
----
git clone https://github.com/usetrmnl/terminus
cd terminus
bin/setup
----
=== Console
To access the console with direct access to all objects, run:
[source,bash]
----
bin/console
----
Once in the console, you can do the following:
[source,ruby]
----
# Use a repository.
repository = Terminus::Repositories::Device.new
repository.all # View all devices.
repository.find 1 # Find by Device ID.
# Fetch upcoming render, sorts in descending order by created timestamp.
Terminus::Images::Fetcher.new.call images_uri: "https://localhost:2443/assets/images"
# To generate image with random name.
creator = Terminus::Images::Creator.new
creator.call "<p>Test</p>",
Pathname(Hanami.app[:settings].images_root).join("generated/%<name>s.bmp")
#<Pathname:terminus/public/assets/images/generated/f5af3f06-775f-4ae9-8bb1-246d9a5200c9.bmp>
# To generate image with specific name.
creator.call "<p>Test.</p>", Pathname.pwd.join("demo.bmp")
#<Pathname:terminus/public/assets/images/generated/demo.bmp>
----
When creating images, you might find this HTML template valuable as a starting point as this let's you use the full capabilities of HTML to create new images for your device.
.HTML Template
[%collapsible]
====
[source,html]
----
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<title>Demo</title>
<meta charset="utf-8">
<style type="text/css">
* {
margin: 0;
}
</style>
<script type="text/javascript">
</script>
</head>
<body>
<img src="uri/to/image" alt="Image"/>
</body>
</html>
----
====
Use of `margin` zero is important to prevent default browser styles from creating borders around your image which will show up when rendered on your device. Otherwise, you have full capabilities to render any kind of page you want using whatever HTML you like. Anything is possible because `Images::Creator` is designed to screenshot your rendered HTML as a 800x480 image to render on your device. If you put all this together, that means you can do this in the console:
.Image Generation
[%collapsible]
====
[source,ruby]
----
creator = Terminus::Images::Creator.new
creator.call(<<~CONTENT, Pathname(Hanami.app[:settings].images_root).join("generated/%<name>s.bmp"))
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<title>Demo</title>
<meta charset="utf-8">
<style type="text/css">
* {
margin: 0;
}
</style>
<script type="text/javascript">
</script>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
CONTENT
----
====
The above will create a new image in the `public/images/generated` folder of this application which will eventually render on your device. π
To build a {docker_link} image, run:
[source,bash]
----
bin/docker/build
----
To work within your {docker_link} image, run:
[source,bash]
----
bin/docker/console
----
=== YJIT
{yjit_link} is enabled by default if detected which means you have built and installed Ruby with YJIT enabled. If you didn't build Ruby with YJIT support, YJIT support will be ignored. That said, we _recommend_ you enable YJIT support since the performance improvements are worth it.
π‘ To enable YJIT globally, ensure the `--yjit` flag is added to your `RUBYOPT` environment variable. Example: `export RUBYOPT="--yjit"`.
=== CSS
Pure CSS is used since modern CSS is so powerful we don't need to reach for frameworks anymore which also reduces the complexity of this project. The following stylesheets allow you to customize the look and feel of this application as follows:
* *Colors*: Use to customize site colors.
* *Defaults*: Use to customize HTML element defaults.
* *Layout*: Use to customize the site layout.
* *Settings*: Use to customize site settings.
* *View Transitions*: Use to customize view transitions.
For responsive resolutions, the following measuresments are used:
* *Extra Small*: 300px
* *Small*: 500px
* *Medium*: 825px
* *Large*: 1000px
* *Extra Large*: 1500px
== Tests
To test, run:
[source,bash]
----
bin/rake
----
== Deployment
*Local*
. Configure `APP_URL` within `+*.env+` file(s) to where your app is hosted (i.e. `https://192.168.x.x:2443`). π‘ Lack of trailing slash is important.
. Retrieve your machine's local IP, ex 192.168.x.x (Mac: `ifconfig | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}'`).
. Set `APP_URL` as the Custom Server via the [Firmware's](https://github.com/usetrmnl/firmware) WiFi Captive Portal
*Hosted*
More details to be provided soon.
== Credits
* Built with link:https://alchemists.io/projects/hanamismith[Hanamismith].
* Engineered by {trmnl_link}.
Languages
Ruby
76.6%
HTML
16.1%
PLpgSQL
3.9%
CSS
2.1%
Shell
0.8%
Other
0.5%