Talk to USB device from Phoenix app


Do y’all know of a way to talk to a USB device from a Phoenix web app?

1 Like

Look into the Web USB API. (It doesn’t have great compatibility)

1 Like

Maybe the nerves project has something for this? And also what is the use case?

1 Like

From a Phoenix web app I want to talk to a ledger nano s/x ( ledger nano s and x are crypto currency hardware wallets).

Here is a JS lib for what I’m trying to do: GitHub - LedgerHQ/ledgerjs: Ledger's JavaScript libraries

If you’re trying to talk to the device from the browser, then what I would do is just use the JavaScript library for my web app, and if I need to integrate it with LiveView, I’d use JS hooks. I don’t think there is a better way to do that for someone accessing your phoenix app via a browser since what you’re doing is interacting with the client-side hardware.

So via the JS hooks I can call the ledger js library from my Phoenix code and have it do all the things I need?

I got a simple example working where i can click a button and it pushes an event to a Hook and then said hook does something and then push an event back to the liveview. I am curious if this type of pattern is acceptable.

My end goal is to click a button - have this library do its thing GitHub - LedgerHQ/ledgerjs: Ledger’s JavaScript libraries and then send the results back to my liveview.

So in short i want to click a button in a liveview - have some JS do its thing - and then get the results back in my liveview

Here is my app.js hook code

let Hooks = {};
Hooks.Counter = {
    mounted() {
        this.handleEvent("counter", ({ counter }) => {
            this.pushEvent('inc_2', { counter: counter + 1 })


Here is my counter_live.html.heex

Counter: <%= @counter %>
<button id="test" phx-hook="Counter" phx-click="inc_1">+</button>

Here is counter_live.ex

defmodule MyappWeb.CounterLive do
  # In Phoenix v1.6+ apps, the line below should be: use MyAppWeb, :live_view
  use HelloWeb, :live_view

  def mount(_params, _, socket) do
    {:ok, assign(socket, counter: 0)}

  def handle_event("inc_1", _session, socket) do
    {:noreply, push_event(socket, "counter", %{counter: socket.assigns.counter})}

  def handle_event("inc_2", %{"counter" => counter}, socket) do
    {:noreply, assign(socket, counter: counter)}

You could have the button directly do the thing and send inc_2 to the backend without sending inc_1 at all, that lets you skip a request cycle and makes it snappier. Unless you want the inc_1 step to actually do something on the backend other than send an event back.

1 Like

I think this pattern is where ill land. I believe ill need the backend to build the payload that will be sent to the javascript library.