Skip to content

Commit

Permalink
Merge pull request #25 from usetrmnl/base64_image_flag
Browse files Browse the repository at this point in the history
Base64 image flag
  • Loading branch information
ryanckulp authored Feb 11, 2025
2 parents 5e1579e + 99bcc4c commit c1df4c6
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 112 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Ignore locally generated content
public/images/generated/*.bmp
db/*.sqlite3
db/*.sqlite3-*
.env
20 changes: 11 additions & 9 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# frozen_string_literal: true

source 'https://rubygems.org'

# database, ORM, server
gem 'activerecord'
gem 'dotenv', groups: %i[development test]
gem 'forme'
gem 'json'
gem 'rake'
gem 'sinatra-activerecord'
gem 'pry'
gem 'puma'
gem 'rackup'
gem 'forme'
gem 'pry'
gem 'dotenv', groups: [:development, :test]
gem 'rake'
gem 'sinatra-activerecord'

# browser automation
gem 'ferrum', git: 'https://github.com/rubycdp/ferrum.git', ref: '7cc1a63351232b10f9ce191104efe6e9c72acca2'
Expand All @@ -18,15 +20,15 @@ gem 'puppeteer-ruby', '~> 0.45.6'
# image processing
gem 'mini_magick', '~> 4.12.0'

#db
# db
gem 'sqlite3'

group :development, :test do
gem 'database_cleaner-active_record'
gem 'debug'
gem 'nokogiri'
gem "rspec"
gem "rack-test"
gem "database_cleaner-active_record"
gem 'rack-test'
gem 'rspec'
end

group :test do
Expand Down
2 changes: 0 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ GEM
racc (~> 1.4)
nokogiri (1.18.2-x86_64-darwin)
racc (~> 1.4)
pg (1.5.9)
pry (0.15.2)
coderay (~> 1.1)
method_source (~> 1.0)
Expand Down Expand Up @@ -162,7 +161,6 @@ DEPENDENCIES
json
mini_magick (~> 4.12.0)
nokogiri
pg
pry
puma
puppeteer-ruby (~> 0.45.6)
Expand Down
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require './app'
require 'sinatra/activerecord/rake'

Expand Down
22 changes: 13 additions & 9 deletions app.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'dotenv/load'

require 'sinatra'
Expand All @@ -17,20 +19,20 @@
end

helpers do
def create_forme(model, is_edit, attrs={}, options={})
def create_forme(model, _is_edit, attrs = {}, options = {})
attrs[:method] = :post
options = TailwindConfig.options.merge(options)
if model && model.persisted?
if model&.persisted?
attrs[:action] += "/#{model.id}" if model.id
options[:before] = -> (form) {
options[:before] = lambda { |form|
TailwindConfig.before.call(form)
form.to_s << '<input name="_method" value="patch" type="hidden"/>'
}
end
f = Forme::Form.new(model, options)
f.extend(ExplicitFormePlugin)
f.form_tag(attrs)
return f
f
end
end

Expand All @@ -45,8 +47,8 @@ def create_forme(model, is_edit, attrs={}, options={})
# DEVICE MANAGEMENT
def devices_form(device)
create_forme(device, device.persisted?,
{autocomplete:"off", action: "/devices"},
{namespace: "device"})
{ autocomplete: 'off', action: '/devices' },
{ namespace: 'device' })
end

get '/devices/?' do
Expand Down Expand Up @@ -84,7 +86,7 @@ def devices_form(device)
end

get '/' do
erb :"index"
erb :index
end

# FIRMWARE SETUP
Expand All @@ -97,7 +99,7 @@ def devices_form(device)
api_key = @device.api_key
friendly_id = @device.friendly_id
image_url = "#{ENV['BASE_URL']}/images/setup/setup-logo.bmp"
message = "Welcome to TRMNL BYOS"
message = 'Welcome to TRMNL BYOS'

{ status:, api_key:, friendly_id:, image_url:, message: }.to_json
else
Expand All @@ -109,9 +111,11 @@ def devices_form(device)
get '/api/display/' do
content_type :json
@device = Device.find_by_api_key(env['HTTP_ACCESS_TOKEN'])
screen = ScreenFetcher.call

if @device
base64 = (env['HTTP_BASE64'] || params[:base64]) == 'true'
screen = ScreenFetcher.call(base64:)

{
status: 0, # on core TRMNL server, status 202 loops device back to /api/setup unless User is connected, which doesn't apply here
image_url: screen[:image_url],
Expand Down
4 changes: 3 additions & 1 deletion config/environments/test.rb
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
config.active_record.maintain_test_schema = true
# frozen_string_literal: true

config.active_record.maintain_test_schema = true
22 changes: 12 additions & 10 deletions config/initializers/explicit_forme_plugin.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
# frozen_string_literal: true

module ExplicitFormePlugin
attr_reader :_form_tag

def form_tag(attrs)
@_form_tag = ::Forme::Tag.new(self, :form, attrs)
end

def explicit_open()
@to_s << self.serialize_open(@_form_tag)
self.before_form_yield
def explicit_open
@to_s << serialize_open(@_form_tag)
before_form_yield
@to_s
end

def explicit_close()
def explicit_close
form_len = @to_s.length
self.after_form_yield
@to_s << self.serialize_close(@_form_tag)
after_form_yield
@to_s << serialize_close(@_form_tag)
@to_s[form_len..]
end

def form_output(&block)
form_str = String.new
form_str << self.explicit_open()
form_str << explicit_open
form_str << block.call if block_given?
form_str << self.explicit_close()
form_str << explicit_close
form_str
end
end
end
45 changes: 23 additions & 22 deletions config/initializers/tailwind_form.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
# frozen_string_literal: true

require 'forme'
require 'pp'

module TailwindConfig
def self.label_attr
{
class: 'block text-gray-700 text-md leading-4 font-medium mb-2'
def self.label_attr
{
class: 'block text-gray-700 text-md leading-4 font-medium mb-2'
}
end

def self.before
-> (form) {
def self.before
lambda { |form|
form.to_s << '<div class="rounded-xl bg-gray-50 shadow-sm border mb-6 divide-y divide-gray-200">'
}
end

def self.after
-> (form) {
def self.after
lambda { |form|
form.to_s << '</div>'
}
end
Expand All @@ -24,40 +25,40 @@ def self.options
{
labeler: :explicit,
wrapper: :div,
before: self.before,
after: self.after,
before:,
after:,
input_defaults: {
text: {
label_attr: self.label_attr,
label_attr:,
wrapper_attr: { class: 'p-6' },
class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150' ,
class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150'
},

number: {
label_attr: self.label_attr,
label_attr:,
wrapper_attr: { class: 'p-6' },
class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150' ,
class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150'
},

time: {
label_attr: self.label_attr,
label_attr:,
wrapper_attr: { class: 'p-6' },
class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150' ,
class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150'
},

checkbox: {
label_position: :before,
label_attr: self.label_attr,
label_attr:,
wrapper_attr: { class: 'p-6' },
class: 'h-8 w-8 rounded-full shadow',
class: 'h-8 w-8 rounded-full shadow'
},

submit: {
label_attr: self.label_attr,
label_attr:,
attr: { class: 'cursor-pointer font-medium rounded-lg text-sm px-3 py-2 inline-flex items-center transition duration-150 justify-center shrink-0 gap-1.5 whitespace-nowrap text-white bg-blue-500 hover:bg-blue-600 focus:outline-none' },
wrapper_attr: { class: 'p-6 text-right' },
},
},
wrapper_attr: { class: 'p-6 text-right' }
}
}
}
end
end
2 changes: 2 additions & 0 deletions db/migrate/20241212172428_create_devices.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

class CreateDevices < ActiveRecord::Migration[8.0]
def change
create_table :devices do |t|
Expand Down
23 changes: 11 additions & 12 deletions db/schema.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
Expand All @@ -10,17 +12,14 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[8.0].define(version: 2024_12_12_172428) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"

create_table "devices", force: :cascade do |t|
t.string "name"
t.string "mac_address"
t.string "api_key"
t.string "friendly_id"
t.integer "refresh_interval", default: 900, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
ActiveRecord::Schema[8.0].define(version: 20_241_212_172_428) do
create_table 'devices', force: :cascade do |t|
t.string 'name'
t.string 'mac_address'
t.string 'api_key'
t.string 'friendly_id'
t.integer 'refresh_interval', default: 900, null: false
t.datetime 'created_at', null: false
t.datetime 'updated_at', null: false
end
end
7 changes: 4 additions & 3 deletions models/device.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class Device < ActiveRecord::Base
# frozen_string_literal: true

DEFAULT_DEVICE_NAME = 'My TRMNL'.freeze
class Device < ActiveRecord::Base
DEFAULT_DEVICE_NAME = 'My TRMNL'
MODEL = [
{
name: 'og',
Expand All @@ -21,4 +22,4 @@ def generate_friendly_id
def generate_api_key
self.api_key = SecureRandom.urlsafe_base64(nil, false)
end
end
end
22 changes: 17 additions & 5 deletions services/screen_fetcher.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
# frozen_string_literal: true

class ScreenFetcher
class << self
def call
attr_reader :base64

def call(base64: false)
@base64 = base64
last_generated_image || empty_state_image
end

def last_generated_image
img_path = Dir.glob(File.join(base_path, '*.*')).max { |a, b| File.ctime(a) <=> File.ctime(b) }
return nil unless img_path
full_img_path = Dir.glob(File.join(base_path, '*.*')).max { |a, b| File.ctime(a) <=> File.ctime(b) }
return nil unless full_img_path

filename = File.basename(full_img_path) # => 1as4ff.bmp
relative_img_path = "images/generated/#{filename}"

filename = img_path.split('/').last # => 1as4ff.bmp
image_url = "#{base_domain}/images/generated/#{filename}"
image_url = if base64
img = File.open("public/#{relative_img_path}")
"data:image/png;base64,#{Base64.strict_encode64(img.read)}"
else
"#{base_domain}/#{relative_img_path}"
end

{ filename:, image_url: }
end
Expand Down
Loading

0 comments on commit c1df4c6

Please sign in to comment.