Packages
mob_dev
0.2.9
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.37
0.3.35
0.3.34
0.3.33
0.3.28
0.3.26
0.3.23
0.3.21
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.18
0.2.17
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
Development tooling for the Mob mobile framework
Current section
Files
Jump to
Current section
Files
lib/mob_dev/device.ex
defmodule MobDev.Device do
@moduledoc """
Represents a connected or available device (physical or emulator/simulator).
"""
@type t :: %__MODULE__{}
@enforce_keys [:platform, :serial]
defstruct [
:platform, # :android | :ios
:serial, # "emulator-5554" | "R5CW3089HVB" | "78354490-EF38-..."
:name, # "Pixel 8" | "iPhone 17"
:version, # "Android 15" | "iOS 18"
:type, # :emulator | :simulator | :physical
:node, # :"mob_demo_android@127.0.0.1"
:dist_port, # 9100
:status, # :discovered | :unauthorized | :tunneled | :connected | :error
:error # error message string if status == :error
]
@doc """
Derives a short identifier from a serial for use in node names.
iex> MobDev.Device.short_id("emulator-5554")
"5554"
iex> MobDev.Device.short_id("R5CW3089HVB")
"HVBA" # last 4 chars, uppercased
iex> MobDev.Device.short_id("78354490-EF38-44D7-A437-DD941C20524D")
"524D"
"""
@spec short_id(String.t()) :: String.t()
def short_id(serial) do
serial
|> String.replace("-", "")
|> String.slice(-4..-1)
|> String.upcase()
end
@doc """
Returns the Erlang node name atom for a device.
Uses 127.0.0.1 for USB-connected devices (tunneled).
Node names are `<app>_<platform>@127.0.0.1` where `<app>` is the OTP
application name from the current Mix project (e.g. `my_app_android@127.0.0.1`).
Multi-device support (where unique per-device names are needed) is future work
and will require the app to receive its node name dynamically via intent extras.
"""
@spec node_name(t()) :: atom()
def node_name(%__MODULE__{platform: :android}) do
:"#{app_name()}_android@127.0.0.1"
end
def node_name(%__MODULE__{platform: :ios}) do
:"#{app_name()}_ios@127.0.0.1"
end
defp app_name, do: Mix.Project.config()[:app]
@doc "Human-readable one-line summary."
@spec summary(t()) :: String.t()
def summary(%__MODULE__{} = d) do
type_label = case d.type do
:emulator -> "emulator"
:simulator -> "simulator"
:physical -> "physical"
nil -> "device"
end
status_icon = case d.status do
:connected -> "✓"
:tunneled -> "⟳"
:discovered -> "·"
:unauthorized -> "✗"
:error -> "!"
_ -> "?"
end
name = d.name || d.serial
version = if d.version, do: " (#{d.version})", else: ""
"#{status_icon} #{name}#{version} [#{type_label}] #{d.serial}"
end
end