Module nh_agent

The device agent: socket, channel, heartbeat, and dispatch.

Description

The device agent: socket, channel, heartbeat, and dispatch.

This is the only process. nh_channel holds the protocol and nh_metadata reads the firmware description; both are pure, and this loop is what gives them a socket and a clock.

The transport

The transport is a module, not a hard dependency, so the agent can be driven on a desktop against a real NervesHub before it runs on a chip:

  Transport:open(Config)             -> {ok, Handle} | {error, term()}
  Transport:send_text(Handle, Binary) -> ok | {error, term()}
  Transport:close(Handle)            -> ok

and it delivers {websocket, Handle, connected | {text, B} | {closed, R} | {error, R}}. websocket_client from atomvm_websocket_client already has this shape.

Reconnection

The transport reconnects on its own, so connected arrives more than once. Every one of them starts a fresh channel join, because a Phoenix channel does not survive a socket reconnect. This is the whole reason the agent watches for connected rather than joining once at startup.

Heartbeats

NervesHub closes a socket that stops sending heartbeats. The deadline is kept as an absolute time rather than a receive ... after timeout, so a steady stream of incoming messages cannot keep pushing the heartbeat out and get the device disconnected while it looks busy and healthy.

Data Types

config()

config() = #{url => binary() | string(), host => binary() | string(), identifier := binary(), transport => module(), shared_secret => {binary(), binary()}, client_cert => {binary(), binary()}, verify => term(), metadata => map(), handler => pid(), heartbeat_ms => pos_integer()}

Function Index

start/1
start_link/1Start the agent, linked to the caller.
stop/1

Function Details

start/1

start(Config::config()) -> {ok, pid()} | {error, term()}

start_link/1

start_link(Config::config()) -> {ok, pid()} | {error, term()}

Start the agent, linked to the caller.

stop/1

stop(Pid::pid()) -> ok


Generated by EDoc