USP Agent: Device-Side Implementation
Overview
The USP Agent represents a device (like a router or gateway) that responds to Controller requests. This livebook demonstrates creating and using a USP Agent.
Setup
Mix.install([
{:caretaker, path: "."}
])
alias Caretaker.USP.{Agent, Proto, Record}
require Logger
Starting an Agent
An Agent is started with an endpoint ID and optional initial data:
# Start an agent with device data
{:ok, agent} = Agent.start_link(
endpoint_id: "os::ACME-Router-001",
initial_data: %{
"Device" => %{
"DeviceInfo" => %{
"Manufacturer" => "ACME Corp",
"ModelName" => "Router-X100",
"SerialNumber" => "SN-001",
"SoftwareVersion" => "1.5.2",
"HardwareVersion" => "1.0"
},
"ManagementServer" => %{
"EnableCWMP" => "false",
"URL" => ""
},
"WiFi" => %{
"RadioNumberOfEntries" => "2",
"SSIDNumberOfEntries" => "4"
}
}
}
)
# Get the agent's endpoint ID
endpoint_id = Agent.endpoint_id(agent)
Logger.info("Agent started: #{endpoint_id}")
Handling Get Requests
Send a Get request to the agent:
# Build a Get message
get_msg = Proto.build_get(["Device.DeviceInfo.Manufacturer", "Device.DeviceInfo.ModelName"])
# Agent processes the message and returns a response
{:ok, response} = Agent.handle_message(agent, get_msg)
# The response is a GetResp message
Logger.info("Response type: #{inspect(response.body)}")
response
Handling Set Requests
Update parameter values:
# Build a Set message
set_msg = Proto.build_set([
{"Device.ManagementServer.", [
{"EnableCWMP", "true"},
{"URL", "http://acs.example.com/cwmp"}
]}
])
# Process the Set request
{:ok, set_response} = Agent.handle_message(agent, set_msg)
Logger.info("Set completed")
set_response
Handling GetSupportedDM Requests
Query the data model:
# Build GetSupportedDM message
get_dm_msg = Proto.build_get_supported_dm(["Device.DeviceInfo."])
# Process the request
{:ok, dm_response} = Agent.handle_message(agent, get_dm_msg)
dm_response
Handling GetInstances Requests
List object instances:
# Build GetInstances message
get_instances_msg = Proto.build_get_instances(["Device."])
# Process the request
{:ok, instances_response} = Agent.handle_message(agent, get_instances_msg)
instances_response
Register Message
Agents send Register messages to announce themselves to Controllers:
# Build a Register message for the agent
register_msg = Agent.build_register_message(agent)
Logger.info("Register message ID: #{Proto.message_id(register_msg)}")
register_msg
Agent with Full Message Flow
Simulate a complete request/response cycle:
# Create a new agent with more data
{:ok, router} = Agent.start_link(
endpoint_id: "os::TestRouter-123",
initial_data: %{
"Device" => %{
"DeviceInfo" => %{
"Manufacturer" => "TestCorp",
"ProductClass" => "Router",
"SerialNumber" => "TEST123"
},
"WiFi" => %{
"Radio" => %{
"1" => %{
"Enable" => "true",
"Channel" => "6",
"OperatingFrequencyBand" => "2.4GHz"
},
"2" => %{
"Enable" => "true",
"Channel" => "36",
"OperatingFrequencyBand" => "5GHz"
}
}
}
}
}
)
# Simulate Controller sending Get request
controller_id = "self::controller.example.com"
agent_id = Agent.endpoint_id(router)
# Build Get message
get_msg = Proto.build_get(["Device.WiFi.Radio.1.Channel", "Device.WiFi.Radio.2.Channel"])
# Wrap in Record
request_record = Record.new(get_msg,
to_id: agent_id,
from_id: controller_id
)
# Encode for transport
{:ok, encoded_request} = Record.encode(request_record)
Logger.info("Request size: #{byte_size(encoded_request)} bytes")
# Agent receives and decodes
{:ok, received_record} = Record.decode(encoded_request)
{:ok, received_msg} = Record.extract_message(received_record)
# Agent processes and responds
{:ok, response_msg} = Agent.handle_message(router, received_msg)
# Wrap response in Record
response_record = Record.response_for(received_record, response_msg)
# Encode response for transport
{:ok, encoded_response} = Record.encode(response_record)
Logger.info("Response size: #{byte_size(encoded_response)} bytes")
# Controller receives response
{:ok, final_record} = Record.decode(encoded_response)
{:ok, final_msg} = Record.extract_message(final_record)
%{
request_id: Proto.message_id(get_msg),
response_id: Proto.message_id(final_msg),
from: final_record.from_id,
to: final_record.to_id
}
Cleanup
# Stop the agents
GenServer.stop(agent)
GenServer.stop(router)
Logger.info("Agents stopped")
Agent Design Notes
The USP Agent:
- Uses DeviceState - Integrates with existing TR-181 data model storage
- Handles All Message Types - Get, Set, Add, Delete, Operate, etc.
- Generates Responses - Builds appropriate response messages
- Supports Register - Announces itself to Controllers
Summary
The USP Agent provides device-side protocol handling:
-
Start with
Agent.start_link/1 -
Process messages with
Agent.handle_message/2 -
Build Register messages with
Agent.build_register_message/1 - Integrates with existing TR-181 data model
Next: See 16_usp_controller.livemd for Controller implementation.