Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion lib/trmnl/inventory/device.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ defmodule Trmnl.Inventory.Device do
field :latest_screen, :string
field :mac_address, :string
field :name, :string, default: "My TRMNL"
field :pixel_width, :integer, default: 800
field :pixel_height, :integer, default: 480
field :rotation, :integer, default: 90
field :playlist_index, :integer, default: 0
field :refresh_interval, :integer, default: 900
field :screen_generated_at, :utc_datetime
Expand All @@ -26,6 +29,9 @@ defmodule Trmnl.Inventory.Device do
:latest_screen,
:mac_address,
:name,
:pixel_width,
:pixel_height,
:rotation,
:playlist_index,
:refresh_interval,
:screen_generated_at
Expand All @@ -50,7 +56,9 @@ defmodule Trmnl.Inventory.Device do

defp upcase(changeset, fields) do
Enum.reduce(fields, changeset, fn field, changeset ->
update_change(changeset, field, &String.upcase/1)
update_change(changeset, field, fn value ->
if value, do: String.upcase(value), else: value
end)
end)
end
end
10 changes: 5 additions & 5 deletions lib/trmnl/screen.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ defmodule Trmnl.Screen do
require Logger
alias Trmnl.Inventory

@width 800
@height 480
@color_depth 1

@doc """
Expand Down Expand Up @@ -49,9 +47,9 @@ defmodule Trmnl.Screen do
# --- Generate screenshot ---
System.cmd("puppeteer-img", [
"--width",
to_string(@width),
to_string(device.pixel_width),
"--height",
to_string(@height),
to_string(device.pixel_height),
"--path",
screenshot_path,
current_screen_url
Expand All @@ -61,7 +59,9 @@ defmodule Trmnl.Screen do
System.cmd("magick", [
screenshot_path,
"-resize",
"#{@width}x#{@height}",
"#{device.pixel_width}x#{device.pixel_height}",
"-rotate",
"#{device.rotation}",
"-dither",
"FloydSteinberg",
"-remap",
Expand Down
2 changes: 1 addition & 1 deletion lib/trmnl_web/components/core_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ defmodule TrmnlWeb.CoreComponents do
def simple_form(assigns) do
~H"""
<.form :let={f} for={@for} as={@as} {@rest}>
<div class="mt-10 space-y-8 bg-white">
<div class="mt-10 space-y-3 bg-white">
{render_slot(@inner_block, f)}
<div :for={action <- @actions} class="mt-2 flex items-center justify-between gap-6">
{render_slot(action, f)}
Expand Down
2 changes: 1 addition & 1 deletion lib/trmnl_web/components/layouts/screen.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<link rel="stylesheet" href={~p"/assets/app.css"} />
</head>
<body class="w-[800px] h-[480px]">
<body class={"w-[#{@device.pixel_width}px] h-[#{@device.pixel_height}px]"}>
{@inner_content}
</body>
</html>
3 changes: 3 additions & 0 deletions lib/trmnl_web/live/device_live/form_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ defmodule TrmnlWeb.DeviceLive.FormComponent do
<.input field={@form[:mac_address]} type="text" label="MAC address" />
<.input field={@form[:api_key]} type="text" label="API key" />
<.input field={@form[:friendly_id]} type="text" label="Friendly ID" />
<.input field={@form[:pixel_height]} type="number" label="Device height (px)" />
<.input field={@form[:pixel_width]} type="number" label="Device width (px)" />
<.input field={@form[:rotation]} type="number" label="Rotation" />
<.input field={@form[:refresh_interval]} type="number" label="Refresh interval" />
<:actions>
<.button phx-disable-with="Saving...">Save Device</.button>
Expand Down
5 changes: 5 additions & 0 deletions lib/trmnl_web/live/device_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ defmodule TrmnlWeb.DeviceLive.Show do
|> assign(:screen_at, DateTime.to_unix(DateTime.utc_now()))}
end

@impl true
def handle_info({TrmnlWeb.DeviceLive.FormComponent, {:saved, device}}, socket) do
{:noreply, assign(socket, :device, device)}
end

@impl true
def handle_event("regenerate", _, socket) do
{:ok, device} = Screen.regenerate(socket.assigns.device)
Expand Down
3 changes: 3 additions & 0 deletions lib/trmnl_web/live/device_live/show.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
<:item title="Friendly ID">{@device.friendly_id}</:item>
<:item title="Alive at">{@device.alive_at || "Never"}</:item>
<:item title="Refresh interval">{@device.refresh_interval}</:item>
<:item title="Device height (px)">{@device.pixel_height}</:item>
<:item title="Device width (px)">{@device.pixel_width}</:item>
<:item title="Rotation">{@device.rotation}</:item>
</.list>

<%= if @device.latest_screen do %>
Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"},
"floki": {:hex, :floki, "0.37.0", "b83e0280bbc6372f2a403b2848013650b16640cd2470aea6701f0632223d719e", [:mix], [], "hexpm", "516a0c15a69f78c47dc8e0b9b3724b29608aa6619379f91b1ffa47109b5d0dd3"},
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
"heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized"]},
"heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized", depth: 1]},
"hpax": {:hex, :hpax, "1.0.2", "762df951b0c399ff67cc57c3995ec3cf46d696e41f0bba17da0518d94acd4aac", [:mix], [], "hexpm", "2f09b4c1074e0abd846747329eaa26d535be0eb3d189fa69d812bfb8bfefd32f"},
"httpoison": {:hex, :httpoison, "2.2.1", "87b7ed6d95db0389f7df02779644171d7319d319178f6680438167d7b69b1f3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "51364e6d2f429d80e14fe4b5f8e39719cacd03eb3f9a9286e61e216feac2d2df"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
Expand Down
11 changes: 11 additions & 0 deletions priv/repo/migrations/20260213145155_add_dimensions_to_devices.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule Trmnl.Repo.Migrations.AddDimensionsToDevices do
use Ecto.Migration

def change do
alter table(:devices) do
add :pixel_width, :integer, default: 800, null: false
add :pixel_height, :integer, default: 480, null: false
add :rotation, :integer, default: 90, null: false
end
end
end
3 changes: 2 additions & 1 deletion test/support/fixtures/inventory_fixtures.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ defmodule Trmnl.InventoryFixtures do
@doc """
Generate a unique device mac_address.
"""
def unique_device_mac_address, do: "some mac_address#{System.unique_integer([:positive])}"
def unique_device_mac_address, do: Enum.map(1..6, fn _ -> :rand.uniform(255) |> Integer.to_string(16) |> String.pad_leading(2, "0") end) |> Enum.join(":")

@doc """
Generate a device.
"""
def device_fixture(attrs \\ %{}) do

{:ok, device} =
attrs
|> Enum.into(%{
Expand Down
20 changes: 13 additions & 7 deletions test/trmnl/inventory_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defmodule Trmnl.InventoryTest do

import Trmnl.InventoryFixtures

@invalid_attrs %{name: nil, api_key: nil, mac_address: nil, friendly_id: nil, refresh_interval: nil}
@invalid_attrs %{name: nil, api_key: nil, mac_address: nil, friendly_id: nil, refresh_interval: nil, rotation: nil}

test "list_devices/0 returns all devices" do
device = device_fixture()
Expand All @@ -21,14 +21,17 @@ defmodule Trmnl.InventoryTest do
end

test "create_device/1 with valid data creates a device" do
valid_attrs = %{name: "some name", api_key: "some api_key", mac_address: "some mac_address", friendly_id: "some friendly_id", refresh_interval: 42}
valid_attrs = %{name: "some name", api_key: "some api_key", mac_address: "AA:BB:CC:DD:EE:FF", friendly_id: "some friendly_id", refresh_interval: 42, rotation: 0}

assert {:ok, %Device{} = device} = Inventory.create_device(valid_attrs)
assert device.name == "some name"
assert device.api_key == "some api_key"
assert device.mac_address == "some mac_address"
assert device.friendly_id == "some friendly_id"
assert device.mac_address == "AA:BB:CC:DD:EE:FF"
assert device.friendly_id == "SOME FRIENDLY_ID"
assert device.refresh_interval == 42
assert device.pixel_width == 800
assert device.pixel_height == 480
assert device.rotation == 0
end

test "create_device/1 with invalid data returns error changeset" do
Expand All @@ -37,14 +40,17 @@ defmodule Trmnl.InventoryTest do

test "update_device/2 with valid data updates the device" do
device = device_fixture()
update_attrs = %{name: "some updated name", api_key: "some updated api_key", mac_address: "some updated mac_address", friendly_id: "some updated friendly_id", refresh_interval: 43}
update_attrs = %{name: "some updated name", api_key: "some updated api_key", mac_address: "FF:EE:DD:CC:BB:AA", friendly_id: "SOME UPDATED FRIENDLY_ID", refresh_interval: 43, pixel_height: 1080, pixel_width: 1920, rotation: 90}

assert {:ok, %Device{} = device} = Inventory.update_device(device, update_attrs)
assert device.name == "some updated name"
assert device.api_key == "some updated api_key"
assert device.mac_address == "some updated mac_address"
assert device.friendly_id == "some updated friendly_id"
assert device.mac_address == "FF:EE:DD:CC:BB:AA"
assert device.friendly_id == "SOME UPDATED FRIENDLY_ID"
assert device.refresh_interval == 43
assert device.pixel_height == 1080
assert device.pixel_width == 1920
assert device.rotation == 90
end

test "update_device/2 with invalid data returns error changeset" do
Expand Down
2 changes: 1 addition & 1 deletion test/trmnl_web/controllers/page_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ defmodule TrmnlWeb.PageControllerTest do

test "GET /", %{conn: conn} do
conn = get(conn, ~p"/")
assert html_response(conn, 200) =~ "Peace of mind from prototype to production"
assert html_response(conn, 302) =~ "You are being <a href"
end
end
40 changes: 37 additions & 3 deletions test/trmnl_web/live/device_live_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,36 @@ defmodule TrmnlWeb.DeviceLiveTest do
import Phoenix.LiveViewTest
import Trmnl.InventoryFixtures

@create_attrs %{name: "some name", api_key: "some api_key", mac_address: "some mac_address", friendly_id: "some friendly_id", refresh_interval: 42}
@update_attrs %{name: "some updated name", api_key: "some updated api_key", mac_address: "some updated mac_address", friendly_id: "some updated friendly_id", refresh_interval: 43}
@invalid_attrs %{name: nil, api_key: nil, mac_address: nil, friendly_id: nil, refresh_interval: nil}
@create_attrs %{
name: "some name",
api_key: "some api_key",
mac_address: "AA:BB:CC:DD:EE:FF",
friendly_id: "some friendly_id",
refresh_interval: 42,
pixel_height: 400,
pixel_width: 600,
rotation: 0
}
@update_attrs %{
name: "some updated name",
api_key: "some updated api_key",
mac_address: "11:22:33:44:55:66",
friendly_id: "some updated friendly_id",
refresh_interval: 43,
pixel_height: 444,
pixel_width: 555,
rotation: 90
}
@invalid_attrs %{
name: nil,
api_key: nil,
mac_address: nil,
friendly_id: nil,
refresh_interval: nil,
pixel_height: nil,
pixel_width: nil,
rotation: nil
}

defp create_device(_) do
device = device_fixture()
Expand Down Expand Up @@ -84,7 +111,11 @@ defmodule TrmnlWeb.DeviceLiveTest do
{:ok, _show_live, html} = live(conn, ~p"/devices/#{device}")

assert html =~ "Show Device"
assert html =~ device.friendly_id
assert html =~ device.name
assert html =~ "#{device.pixel_height}"
assert html =~ "#{device.pixel_width}"
assert html =~ "#{device.rotation}"
end

test "updates device within modal", %{conn: conn, device: device} do
Expand All @@ -108,6 +139,9 @@ defmodule TrmnlWeb.DeviceLiveTest do
html = render(show_live)
assert html =~ "Device updated successfully"
assert html =~ "some updated name"
assert html =~ "444"
assert html =~ "555"
assert html =~ "90"
end
end
end