From 8096f460573b9999cd365ff5aaab2d80cb177848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Fri, 3 Apr 2026 23:09:15 +0200 Subject: [PATCH 1/6] add qr code support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pepe Márquez Romero --- config/config.exs | 2 + lib/name_badge/screen/name_badge.ex | 13 +++++- lib/name_badge/screen/promotional_qrcode.ex | 51 +++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 lib/name_badge/screen/promotional_qrcode.ex diff --git a/config/config.exs b/config/config.exs index 5a271a5..9f22b25 100644 --- a/config/config.exs +++ b/config/config.exs @@ -32,6 +32,8 @@ device_setup_url = config :name_badge, :device_setup_url, device_setup_url +config :name_badge, :qr_link, System.get_env("QR_LINK", "https://nervesmeetup.eu") + # Optional: Calendar sync via iCal secret address (read-only) # Set CALENDAR_URL to your Google Calendar secret iCal address before building. # Set CALENDAR_REFRESH_INTERVAL to customize the refresh interval in milliseconds (default: 5 min). diff --git a/lib/name_badge/screen/name_badge.ex b/lib/name_badge/screen/name_badge.ex index 42f92cb..14ea4d2 100644 --- a/lib/name_badge/screen/name_badge.ex +++ b/lib/name_badge/screen/name_badge.ex @@ -59,7 +59,16 @@ defmodule NameBadge.Screen.NameBadge do case config do %{"first_name" => _first_name, "last_name" => _last_name} -> - {:ok, assign(screen, config: config, valid?: true)} + qr_link = Application.get_env(:name_badge, :qr_link, "https://nervesmeetup.eu") + qr_code = qr_code_for_url(String.trim(qr_link)) + + {:ok, + assign(screen, + config: config, + qr_code: qr_code, + valid?: true, + button_hints: %{b: "Show QR"} + )} _config -> {:ok, assign(screen, valid?: false, button_hints: %{a: "Set up WiFi", b: "View QR code"})} @@ -80,7 +89,7 @@ defmodule NameBadge.Screen.NameBadge do def handle_button(:button_2, :single_press, screen) do cond do screen.assigns.valid? -> - {:noreply, screen} + {:noreply, navigate(screen, NameBadge.Screen.PromotionalQRCode)} true -> {:noreply, navigate(screen, NameBadge.Screen.Settings.QrCode)} diff --git a/lib/name_badge/screen/promotional_qrcode.ex b/lib/name_badge/screen/promotional_qrcode.ex new file mode 100644 index 0000000..96a3156 --- /dev/null +++ b/lib/name_badge/screen/promotional_qrcode.ex @@ -0,0 +1,51 @@ +defmodule NameBadge.Screen.PromotionalQRCode do + use NameBadge.Screen + + @impl NameBadge.Screen + def render(%{qr_code: qr_code}) do + qr_element = + case qr_code do + qr when not is_nil(qr_code) -> + """ + #align(center + horizon)[ + #image(height: 80%, format: "svg", bytes("#{qr_code}")) + + Scan to reach me ! + ] + """ + + _ -> + """ + #align(center + horizon)[ + #text(font: \"New Amsterdam\", size: 24pt)[No QR code available] + ] + """ + end + end + + @impl NameBadge.Screen + def mount(_args, screen) do + qr_link = Application.get_env(:name_badge, :qr_link, "https://nervesmeetup.eu") + qr_code = qr_code_for_url(String.trim(qr_link)) + + {:ok, assign(screen, qr_code: qr_code, button_hints: %{b: "Back to badge"})} + end + + @impl NameBadge.Screen + def handle_button(:button_2, :single_press, screen) do + {:noreply, navigate(screen, :back)} + end + + def handle_button(_, _, screen), do: {:noreply, screen} + + defp qr_code_for_url(url) do + with {:ok, _code} = result <- QRCode.create(url), + {:ok, qr_code_svg} <- QRCode.render(result) do + qr_code_svg + |> String.replace("\\", "\\\\") + |> String.replace("\"", "\\\"") + else + _ -> nil + end + end +end From 6f7bb0bfdf74e67fc07a4385c7af062c4dcfac90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Fri, 3 Apr 2026 23:15:28 +0200 Subject: [PATCH 2/6] remove uncessary logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pepe Márquez Romero --- lib/name_badge/screen/name_badge.ex | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lib/name_badge/screen/name_badge.ex b/lib/name_badge/screen/name_badge.ex index 14ea4d2..a18b021 100644 --- a/lib/name_badge/screen/name_badge.ex +++ b/lib/name_badge/screen/name_badge.ex @@ -59,17 +59,7 @@ defmodule NameBadge.Screen.NameBadge do case config do %{"first_name" => _first_name, "last_name" => _last_name} -> - qr_link = Application.get_env(:name_badge, :qr_link, "https://nervesmeetup.eu") - qr_code = qr_code_for_url(String.trim(qr_link)) - - {:ok, - assign(screen, - config: config, - qr_code: qr_code, - valid?: true, - button_hints: %{b: "Show QR"} - )} - + {:ok, assign(screen, config: config, valid?: true, button_hints: %{b: "Show QR"})} _config -> {:ok, assign(screen, valid?: false, button_hints: %{a: "Set up WiFi", b: "View QR code"})} end From 3a696d7c81f3ff466332061e14b75f23a6d4166e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Fri, 3 Apr 2026 23:17:03 +0200 Subject: [PATCH 3/6] remove unecessary else clause MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pepe Márquez Romero --- lib/name_badge/screen/promotional_qrcode.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/name_badge/screen/promotional_qrcode.ex b/lib/name_badge/screen/promotional_qrcode.ex index 96a3156..1a65e27 100644 --- a/lib/name_badge/screen/promotional_qrcode.ex +++ b/lib/name_badge/screen/promotional_qrcode.ex @@ -44,8 +44,6 @@ defmodule NameBadge.Screen.PromotionalQRCode do qr_code_svg |> String.replace("\\", "\\\\") |> String.replace("\"", "\\\"") - else - _ -> nil end end end From cae1f323074a3b8480fa62cc65f887e239aae354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Fri, 3 Apr 2026 23:26:49 +0200 Subject: [PATCH 4/6] refactor promotional qr code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pepe Márquez Romero --- lib/name_badge/screen/promotional_qrcode.ex | 42 +++++++++++---------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/lib/name_badge/screen/promotional_qrcode.ex b/lib/name_badge/screen/promotional_qrcode.ex index 1a65e27..2612c7d 100644 --- a/lib/name_badge/screen/promotional_qrcode.ex +++ b/lib/name_badge/screen/promotional_qrcode.ex @@ -3,30 +3,30 @@ defmodule NameBadge.Screen.PromotionalQRCode do @impl NameBadge.Screen def render(%{qr_code: qr_code}) do - qr_element = - case qr_code do - qr when not is_nil(qr_code) -> - """ - #align(center + horizon)[ - #image(height: 80%, format: "svg", bytes("#{qr_code}")) - - Scan to reach me ! - ] - """ - - _ -> - """ - #align(center + horizon)[ - #text(font: \"New Amsterdam\", size: 24pt)[No QR code available] - ] - """ - end + case qr_code do + nil -> + """ + #align(center + horizon)[ + #text(font: \"New Amsterdam\", size: 24pt)[No QR code available] + ] + """ + + qr -> + """ + #align(center + horizon)[ + #image(height: 80%, format: "svg", bytes("#{qr}")) + + Scan to reach me ! + ] + """ + end end @impl NameBadge.Screen def mount(_args, screen) do - qr_link = Application.get_env(:name_badge, :qr_link, "https://nervesmeetup.eu") - qr_code = qr_code_for_url(String.trim(qr_link)) + qr_code = + Application.get_env(:name_badge, :qr_link) + |> qr_code_for_url() {:ok, assign(screen, qr_code: qr_code, button_hints: %{b: "Back to badge"})} end @@ -38,6 +38,8 @@ defmodule NameBadge.Screen.PromotionalQRCode do def handle_button(_, _, screen), do: {:noreply, screen} + defp qr_code_for_url(nil), do: nil + defp qr_code_for_url(url) do with {:ok, _code} = result <- QRCode.create(url), {:ok, qr_code_svg} <- QRCode.render(result) do From 634cceb873adfb0a473f1b6986490ca3a3294ef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Mon, 6 Apr 2026 17:42:10 +0200 Subject: [PATCH 5/6] remove promotional qr screen, all in the same screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pepe Márquez Romero --- lib/name_badge/screen/name_badge.ex | 59 +++++++++++++++++++-- lib/name_badge/screen/promotional_qrcode.ex | 51 ------------------ 2 files changed, 56 insertions(+), 54 deletions(-) delete mode 100644 lib/name_badge/screen/promotional_qrcode.ex diff --git a/lib/name_badge/screen/name_badge.ex b/lib/name_badge/screen/name_badge.ex index a18b021..423adf1 100644 --- a/lib/name_badge/screen/name_badge.ex +++ b/lib/name_badge/screen/name_badge.ex @@ -8,6 +8,27 @@ defmodule NameBadge.Screen.NameBadge do @name_size_default 36 @company_size_default 24 + @impl NameBadge.Screen + def render(%{show_qr: true, qr_code: qr_code}) do + case qr_code do + nil -> + """ + #align(center + horizon)[ + #text(font: "New Amsterdam", size: 24pt)[No QR code available] + ] + """ + + qr -> + """ + #align(center + horizon)[ + #image(height: 80%, format: "svg", bytes("#{qr}")) + + Scan to reach me ! + ] + """ + end + end + @impl NameBadge.Screen def render(%{valid?: false}) do """ @@ -59,9 +80,40 @@ defmodule NameBadge.Screen.NameBadge do case config do %{"first_name" => _first_name, "last_name" => _last_name} -> - {:ok, assign(screen, config: config, valid?: true, button_hints: %{b: "Show QR"})} + qr_code = generate_qr_code() + + {:ok, + assign(screen, + config: config, + valid?: true, + qr_code: qr_code, + show_qr: false, + button_hints: %{b: "Show QR"} + )} + _config -> - {:ok, assign(screen, valid?: false, button_hints: %{a: "Set up WiFi", b: "View QR code"})} + {:ok, + assign(screen, + valid?: false, + show_qr: false, + button_hints: %{a: "Set up WiFi", b: "View QR code"} + )} + end + end + + defp generate_qr_code do + Application.get_env(:name_badge, :qr_link) + |> qr_code_for_url() + end + + defp qr_code_for_url(nil), do: nil + + defp qr_code_for_url(url) do + with {:ok, _code} = result <- QRCode.create(url), + {:ok, qr_code_svg} <- QRCode.render(result) do + qr_code_svg + |> String.replace("\\", "\\\\") + |> String.replace("\"", "\\\"") end end @@ -79,7 +131,8 @@ defmodule NameBadge.Screen.NameBadge do def handle_button(:button_2, :single_press, screen) do cond do screen.assigns.valid? -> - {:noreply, navigate(screen, NameBadge.Screen.PromotionalQRCode)} + new_show_qr = !screen.assigns.show_qr + {:noreply, assign(screen, :show_qr, new_show_qr)} true -> {:noreply, navigate(screen, NameBadge.Screen.Settings.QrCode)} diff --git a/lib/name_badge/screen/promotional_qrcode.ex b/lib/name_badge/screen/promotional_qrcode.ex deleted file mode 100644 index 2612c7d..0000000 --- a/lib/name_badge/screen/promotional_qrcode.ex +++ /dev/null @@ -1,51 +0,0 @@ -defmodule NameBadge.Screen.PromotionalQRCode do - use NameBadge.Screen - - @impl NameBadge.Screen - def render(%{qr_code: qr_code}) do - case qr_code do - nil -> - """ - #align(center + horizon)[ - #text(font: \"New Amsterdam\", size: 24pt)[No QR code available] - ] - """ - - qr -> - """ - #align(center + horizon)[ - #image(height: 80%, format: "svg", bytes("#{qr}")) - - Scan to reach me ! - ] - """ - end - end - - @impl NameBadge.Screen - def mount(_args, screen) do - qr_code = - Application.get_env(:name_badge, :qr_link) - |> qr_code_for_url() - - {:ok, assign(screen, qr_code: qr_code, button_hints: %{b: "Back to badge"})} - end - - @impl NameBadge.Screen - def handle_button(:button_2, :single_press, screen) do - {:noreply, navigate(screen, :back)} - end - - def handle_button(_, _, screen), do: {:noreply, screen} - - defp qr_code_for_url(nil), do: nil - - defp qr_code_for_url(url) do - with {:ok, _code} = result <- QRCode.create(url), - {:ok, qr_code_svg} <- QRCode.render(result) do - qr_code_svg - |> String.replace("\\", "\\\\") - |> String.replace("\"", "\\\"") - end - end -end From 9f6cc42318651cbba2db437ed95911f3aba76995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Mon, 13 Apr 2026 20:54:39 +0200 Subject: [PATCH 6/6] format , device setup insecure for testing and qr from form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pepe Márquez Romero --- config/config.exs | 6 ++++-- lib/name_badge/battery.ex | 9 +++++---- lib/name_badge/mocks/battery_mock.ex | 9 +++++---- lib/name_badge/screen/name_badge.ex | 7 ++++--- lib/name_badge/screen/settings/qr_code.ex | 7 ++++++- lib/name_badge/socket.ex | 10 ++++++++-- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/config/config.exs b/config/config.exs index 9f22b25..9e4461a 100644 --- a/config/config.exs +++ b/config/config.exs @@ -30,9 +30,11 @@ device_setup_url = System.get_env("DEVICE_SETUP_URL") || raise "System environment variable `DEVICE_SETUP_URL` was not set. Use value `goatmire.fly.dev`, or set up your own server from the repo at https://github.com/protolux-electronics/goatmire" -config :name_badge, :device_setup_url, device_setup_url +## For local development is useful by default is false +device_setup_insecure = System.get_env("DEVICE_SETUP_INSECURE") == "true" -config :name_badge, :qr_link, System.get_env("QR_LINK", "https://nervesmeetup.eu") +config :name_badge, :device_setup_url, device_setup_url +config :name_badge, :device_setup_insecure, device_setup_insecure # Optional: Calendar sync via iCal secret address (read-only) # Set CALENDAR_URL to your Google Calendar secret iCal address before building. diff --git a/lib/name_badge/battery.ex b/lib/name_badge/battery.ex index 6ad18d3..9a856a1 100644 --- a/lib/name_badge/battery.ex +++ b/lib/name_badge/battery.ex @@ -18,10 +18,11 @@ defmodule NameBadge.Battery do min_voltage = 3.0 max_voltage = 4.2 - percentage = ((v - min_voltage) / (max_voltage - min_voltage) * 100) - |> max(0) - |> min(100) - |> round() + percentage = + ((v - min_voltage) / (max_voltage - min_voltage) * 100) + |> max(0) + |> min(100) + |> round() percentage end diff --git a/lib/name_badge/mocks/battery_mock.ex b/lib/name_badge/mocks/battery_mock.ex index 741ff5c..fa74f9a 100644 --- a/lib/name_badge/mocks/battery_mock.ex +++ b/lib/name_badge/mocks/battery_mock.ex @@ -21,10 +21,11 @@ defmodule NameBadge.BatteryMock do min_voltage = 3.0 max_voltage = 4.2 - percentage = ((v - min_voltage) / (max_voltage - min_voltage) * 100) - |> max(0) - |> min(100) - |> round() + percentage = + ((v - min_voltage) / (max_voltage - min_voltage) * 100) + |> max(0) + |> min(100) + |> round() percentage end diff --git a/lib/name_badge/screen/name_badge.ex b/lib/name_badge/screen/name_badge.ex index 423adf1..537decc 100644 --- a/lib/name_badge/screen/name_badge.ex +++ b/lib/name_badge/screen/name_badge.ex @@ -80,7 +80,7 @@ defmodule NameBadge.Screen.NameBadge do case config do %{"first_name" => _first_name, "last_name" => _last_name} -> - qr_code = generate_qr_code() + qr_code = generate_qr_code(config) {:ok, assign(screen, @@ -101,12 +101,13 @@ defmodule NameBadge.Screen.NameBadge do end end - defp generate_qr_code do - Application.get_env(:name_badge, :qr_link) + defp generate_qr_code(config) do + Map.get(config, "qr_link") |> qr_code_for_url() end defp qr_code_for_url(nil), do: nil + defp qr_code_for_url(""), do: nil defp qr_code_for_url(url) do with {:ok, _code} = result <- QRCode.create(url), diff --git a/lib/name_badge/screen/settings/qr_code.ex b/lib/name_badge/screen/settings/qr_code.ex index d350660..1daffe7 100644 --- a/lib/name_badge/screen/settings/qr_code.ex +++ b/lib/name_badge/screen/settings/qr_code.ex @@ -54,7 +54,12 @@ defmodule NameBadge.Screen.Settings.QrCode do end defp qr_code_for_token(token) do - url = "https://#{device_setup_url()}/device/#{token}/config" + scheme = + if Application.get_env(:name_badge, :device_setup_insecure, false), + do: "http", + else: "https" + + url = "#{scheme}://#{device_setup_url()}/device/#{token}/config" {:ok, qr_code_svg} = url diff --git a/lib/name_badge/socket.ex b/lib/name_badge/socket.ex index 3097dad..6136255 100644 --- a/lib/name_badge/socket.ex +++ b/lib/name_badge/socket.ex @@ -68,6 +68,12 @@ defmodule NameBadge.Socket do @impl Slipstream def handle_call(:connected?, _from, socket), do: {:reply, connected?(socket), socket} - defp config, - do: [uri: "wss://#{Application.get_env(:name_badge, :device_setup_url)}/device/websocket"] + defp config do + url = Application.get_env(:name_badge, :device_setup_url) + + ws_scheme = + if Application.get_env(:name_badge, :device_setup_insecure, false), do: "ws", else: "wss" + + [uri: "#{ws_scheme}://#{url}/device/websocket"] + end end