Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

VintageNet

vintage_net.livemd

VintageNet

Overview

Nerves uses VintageNet to manage network interfaces. VintageNet takes human-readable network configuration descriptions and transforms them into instructions for the Linux kernel, network daemons and other IP networking code whether it’s in Elixir, Erlang, or C.

A second important feature of VintageNet is to monitor network interfaces for internet-connectivity and failures. It automatically switches between network interfaces to prioritize fast and inexpensive interfaces like wired Ethernet, but fall back to slower interfaces like cellular connections when necessary.

Technologies

VintageNet serves as the configuration tooling for various networking technologies, such as Ethernet and WiFi, which must be included as a dependency in your project alongside :vintage_net

There are many officially supported technologies, or you can implement your own with the VintageNet.Technology behavior:

Connecting WiFi

Some technologies, such as VintageNetWiFi provide convenience functions for quick configurations at runtime. Use the inputs below to specify your SSID and password.

If you haven’t connected already, checkout Configure WiFi

Properties

VintageNet maintains a key/value store for networking information. This can be queried and subscribed to for changes. For example, the overall network connection (internet-connected, lan-connected or disconnected) is stored under the "connection" key:

VintageNet.get(["connection"])

This notebook only shows a fraction of properties. For a more complete reference, see VintageNet#Properties.

Network interface-specific keys are scoped by interface name. I.e., ["interface", interface_name, ..]. This shows the IP addresses on an interface. If you’re connected via a USB cable, replace "wlan0" with "usb0". For wired Ethernet, use "eth0". The addresses are all on Erlang tuple form and can be converted to familiar strings using :inet.ntoa/1.

VintageNet.get(["interface", "wlan0", "addresses"])

Each VintageNet technology has its own set of properties and these are documented in the corresponding library. For Wifi, see VintageNetWiFi’s property documentation.

Sometimes it’s helpful to browse all keys using VintageNet.get_by_prefix/1:

VintageNet.get_by_prefix(["interface", "wlan0"])

Bonus

Since VintageNet uses :ets under the hood to store properties, Livebook can render them as a nice table:

Kino.ETS.new(VintageNet)

Property subscriptions

Polling these properties for changes is not ideal so VintageNet supports property subscriptions.

If your device supports WiFi, the following subscribes to the known list of access points, starts a scan going and then waits for the first update.

key = ["interface", "wlan0", "wifi", "access_points"]
VintageNet.subscribe(key)

VintageNet.scan("wlan0")

receive do
  {VintageNet, ^key, _old_value, aps, meta} ->
    IO.inspect(aps, label: "Access Points from Scan")
    IO.inspect(meta, label: "Event meta")
end

VintageNet.unsubscribe(key)

Bonus

Graph the available WiFi networks around you based on their frequency band and signal percentage 👇

alias VegaLite, as: Vl

VintageNet.scan("wlan0")

widget =
  Vl.new(title: "2.4 GHz", width: 300)
  |> Vl.encode_field(:x, "channel", title: "Channel")
  |> Vl.encode_field(:y, "signal_percent", title: "Signal Percent", sort: :descending)
  |> Vl.encode_field(:color, "ssid", title: "SSID")
  |> Vl.mark(:circle, tooltip: [:signal_percent])
  |> Kino.VegaLite.new()
  |> tap(&Kino.render/1)

Kino.VegaLite.periodically(widget, 30000, 0, fn i ->
  aps =
    VintageNet.get(["interface", "wlan0", "wifi", "access_points"])
    |> Enum.filter(fn %{band: band} -> band == :wifi_2_4_ghz end)
    |> Enum.map(&Map.from_struct/1)

  Kino.VegaLite.clear(widget)
  Kino.VegaLite.push_many(widget, aps)

  # Initiate scan for next time
  VintageNet.scan("wlan0")
  {:cont, i + 1}
end)