feat: add Pepwave, Ceragon, Tranzeo, and CMM vendor modules

This commit is contained in:
Graham McIntire 2026-01-22 10:22:19 -06:00
parent 928055cbe3
commit 74e409d6a6
No known key found for this signature in database
10 changed files with 745 additions and 0 deletions

View file

@ -0,0 +1,74 @@
defmodule Towerops.Snmp.Profiles.Vendors.Ceragon do
@moduledoc """
Ceragon CeraOS microwave backhaul device-specific SNMP handling.
Supports Ceragon FibeAir microwave radio systems.
Supported devices:
- FibeAir IP-10 series
- FibeAir IP-20 series
- FibeAir RFU-C/H series
"""
@behaviour Towerops.Snmp.Profiles.Vendors.Vendor
alias Towerops.Snmp.Client
alias Towerops.Snmp.Profiles.Vendors.Vendor
# Ceragon Enterprise OID: 1.3.6.1.4.1.2281
@model_oid "1.3.6.1.4.1.2281.1.1.1.0"
@impl true
def profile_names, do: ["ceraos"]
@impl true
def detect_hardware(client_opts) do
case Client.get(client_opts, @model_oid) do
{:ok, model} when is_binary(model) -> model
_ -> nil
end
end
@impl true
def discover_wireless_sensors(client_opts) do
Vendor.fetch_sensors(wireless_oid_defs(), client_opts)
end
@impl true
def wireless_oid_defs do
[
# TX Power
%{
oid: "1.3.6.1.4.1.2281.10.4.1.11.1.1.1",
sensor_type: "power",
sensor_descr: "TX Power",
sensor_unit: "dBm",
sensor_divisor: 10
},
# RX Level
%{
oid: "1.3.6.1.4.1.2281.10.4.1.11.1.2.1",
sensor_type: "rssi",
sensor_descr: "RX Level",
sensor_unit: "dBm",
sensor_divisor: 10
},
# SNR
%{
oid: "1.3.6.1.4.1.2281.10.4.1.11.1.3.1",
sensor_type: "snr",
sensor_descr: "Signal to Noise Ratio",
sensor_unit: "dB",
sensor_divisor: 10
},
# Link Status (1=up, 2=down)
%{
oid: "1.3.6.1.4.1.2281.10.4.1.11.1.4.1",
sensor_type: "state",
sensor_descr: "Link Status",
sensor_unit: "",
sensor_divisor: 1
}
]
end
end

View file

@ -0,0 +1,74 @@
defmodule Towerops.Snmp.Profiles.Vendors.Cmm do
@moduledoc """
Cambium CMM (Cluster Management Module) device-specific SNMP handling.
Supports Cambium Networks CMM devices for tower site management.
Supported devices:
- CMM3 (legacy)
- CMM4 (current generation)
- CMM5 (latest)
"""
@behaviour Towerops.Snmp.Profiles.Vendors.Vendor
alias Towerops.Snmp.Client
alias Towerops.Snmp.Profiles.Vendors.Vendor
# Cambium Enterprise OID: 1.3.6.1.4.1.161.19
@model_oid "1.3.6.1.4.1.161.19.3.4.4.2.0"
@impl true
def profile_names, do: ["cmm", "cmm4"]
@impl true
def detect_hardware(client_opts) do
case Client.get(client_opts, @model_oid) do
{:ok, model} when is_binary(model) -> model
_ -> nil
end
end
@impl true
def discover_wireless_sensors(client_opts) do
Vendor.fetch_sensors(wireless_oid_defs(), client_opts)
end
@impl true
def wireless_oid_defs do
[
# Power Status (main supply)
%{
oid: "1.3.6.1.4.1.161.19.3.4.4.1.0",
sensor_type: "state",
sensor_descr: "Power Status",
sensor_unit: "",
sensor_divisor: 1
},
# GPS Sync Status
%{
oid: "1.3.6.1.4.1.161.19.3.4.4.3.0",
sensor_type: "state",
sensor_descr: "GPS Sync Status",
sensor_unit: "",
sensor_divisor: 1
},
# System Uptime (seconds)
%{
oid: "1.3.6.1.4.1.161.19.3.4.4.4.0",
sensor_type: "runtime",
sensor_descr: "System Uptime",
sensor_unit: "s",
sensor_divisor: 1
},
# Connected APs count
%{
oid: "1.3.6.1.4.1.161.19.3.4.4.5.0",
sensor_type: "ap-count",
sensor_descr: "Connected APs",
sensor_unit: "",
sensor_divisor: 1
}
]
end
end

View file

@ -0,0 +1,74 @@
defmodule Towerops.Snmp.Profiles.Vendors.Pepwave do
@moduledoc """
Peplink/Pepwave device-specific SNMP handling.
Supports Pepwave cellular routers and bonding devices.
Supported devices:
- Pepwave MAX series (HD2, HD4, Transit, etc.)
- Pepwave Balance series
- Peplink SpeedFusion appliances
"""
@behaviour Towerops.Snmp.Profiles.Vendors.Vendor
alias Towerops.Snmp.Client
alias Towerops.Snmp.Profiles.Vendors.Vendor
# Peplink Enterprise OID: 1.3.6.1.4.1.27662
@model_oid "1.3.6.1.4.1.27662.1.1.1.0"
@impl true
def profile_names, do: ["pepwave"]
@impl true
def detect_hardware(client_opts) do
case Client.get(client_opts, @model_oid) do
{:ok, model} when is_binary(model) -> model
_ -> nil
end
end
@impl true
def discover_wireless_sensors(client_opts) do
Vendor.fetch_sensors(wireless_oid_defs(), client_opts)
end
@impl true
def wireless_oid_defs do
[
# WAN1 Signal Strength
%{
oid: "1.3.6.1.4.1.27662.3.1.1.1.0",
sensor_type: "rssi",
sensor_descr: "WAN1 Signal Strength",
sensor_unit: "dBm",
sensor_divisor: 1
},
# WAN1 Connection Status (1=connected)
%{
oid: "1.3.6.1.4.1.27662.3.1.2.1.0",
sensor_type: "state",
sensor_descr: "WAN1 Status",
sensor_unit: "",
sensor_divisor: 1
},
# CPU Utilization
%{
oid: "1.3.6.1.4.1.27662.2.1.1.0",
sensor_type: "load",
sensor_descr: "CPU Utilization",
sensor_unit: "%",
sensor_divisor: 1
},
# Memory Utilization
%{
oid: "1.3.6.1.4.1.27662.2.1.2.0",
sensor_type: "load",
sensor_descr: "Memory Utilization",
sensor_unit: "%",
sensor_divisor: 1
}
]
end
end

View file

@ -16,8 +16,10 @@ defmodule Towerops.Snmp.Profiles.Vendors.Registry do
alias Towerops.Snmp.Profiles.Vendors.Aviat
alias Towerops.Snmp.Profiles.Vendors.Baicells
alias Towerops.Snmp.Profiles.Vendors.CambiumPtp
alias Towerops.Snmp.Profiles.Vendors.Ceragon
alias Towerops.Snmp.Profiles.Vendors.Ciscowap
alias Towerops.Snmp.Profiles.Vendors.Ciscowlc
alias Towerops.Snmp.Profiles.Vendors.Cmm
alias Towerops.Snmp.Profiles.Vendors.Cnmatrix
alias Towerops.Snmp.Profiles.Vendors.Cnpilot
alias Towerops.Snmp.Profiles.Vendors.Cnwave60
@ -33,6 +35,7 @@ defmodule Towerops.Snmp.Profiles.Vendors.Registry do
alias Towerops.Snmp.Profiles.Vendors.Mimosa
alias Towerops.Snmp.Profiles.Vendors.MoxaAwk
alias Towerops.Snmp.Profiles.Vendors.Netonix
alias Towerops.Snmp.Profiles.Vendors.Pepwave
alias Towerops.Snmp.Profiles.Vendors.Pmp
alias Towerops.Snmp.Profiles.Vendors.Proxim
alias Towerops.Snmp.Profiles.Vendors.Racom
@ -46,6 +49,7 @@ defmodule Towerops.Snmp.Profiles.Vendors.Registry do
alias Towerops.Snmp.Profiles.Vendors.Stellar
alias Towerops.Snmp.Profiles.Vendors.Sub10
alias Towerops.Snmp.Profiles.Vendors.Teltonika
alias Towerops.Snmp.Profiles.Vendors.Tranzeo
alias Towerops.Snmp.Profiles.Vendors.Unifi
alias Towerops.Snmp.Profiles.Vendors.Xirrus
alias Towerops.Snmp.Profiles.Vendors.ZyxelWlc
@ -60,8 +64,10 @@ defmodule Towerops.Snmp.Profiles.Vendors.Registry do
Aviat,
Baicells,
CambiumPtp,
Ceragon,
Ciscowap,
Ciscowlc,
Cmm,
Cnmatrix,
Cnpilot,
Cnwave60,
@ -77,6 +83,7 @@ defmodule Towerops.Snmp.Profiles.Vendors.Registry do
Mimosa,
MoxaAwk,
Netonix,
Pepwave,
Pmp,
Proxim,
Racom,
@ -90,6 +97,7 @@ defmodule Towerops.Snmp.Profiles.Vendors.Registry do
Stellar,
Sub10,
Teltonika,
Tranzeo,
Unifi,
Xirrus,
ZyxelWlc

View file

@ -0,0 +1,83 @@
defmodule Towerops.Snmp.Profiles.Vendors.Tranzeo do
@moduledoc """
Tranzeo outdoor wireless device-specific SNMP handling.
Supports Tranzeo outdoor wireless access points and bridges.
Supported devices:
- Tranzeo TR-5A series
- Tranzeo TR-5plus series
- Tranzeo Classic series
"""
@behaviour Towerops.Snmp.Profiles.Vendors.Vendor
alias Towerops.Snmp.Client
alias Towerops.Snmp.Profiles.Vendors.Vendor
# Tranzeo uses sysDescr for detection
@model_oid "1.3.6.1.2.1.1.1.0"
@impl true
def profile_names, do: ["tranzeo"]
@impl true
def detect_hardware(client_opts) do
case Client.get(client_opts, @model_oid) do
{:ok, descr} when is_binary(descr) ->
if String.contains?(descr, "Tranzeo") do
descr
|> String.split(" ")
|> Enum.drop(1)
|> Enum.take(2)
|> Enum.join(" ")
end
_ ->
nil
end
end
@impl true
def discover_wireless_sensors(client_opts) do
Vendor.fetch_sensors(wireless_oid_defs(), client_opts)
end
@impl true
def wireless_oid_defs do
[
# Associated Clients
%{
oid: "1.3.6.1.4.1.24350.2.1.1.0",
sensor_type: "clients",
sensor_descr: "Associated Clients",
sensor_unit: "",
sensor_divisor: 1
},
# Signal Strength
%{
oid: "1.3.6.1.4.1.24350.2.2.1.0",
sensor_type: "rssi",
sensor_descr: "Signal Strength",
sensor_unit: "dBm",
sensor_divisor: 1
},
# Noise Floor
%{
oid: "1.3.6.1.4.1.24350.2.2.2.0",
sensor_type: "rssi",
sensor_descr: "Noise Floor",
sensor_unit: "dBm",
sensor_divisor: 1
},
# TX Power
%{
oid: "1.3.6.1.4.1.24350.2.2.3.0",
sensor_type: "power",
sensor_descr: "TX Power",
sensor_unit: "dBm",
sensor_divisor: 1
}
]
end
end

View file

@ -0,0 +1,99 @@
defmodule Towerops.Snmp.Profiles.Vendors.CeragonTest do
use Towerops.DataCase, async: true
import Mox
alias Towerops.Snmp.Profiles.Vendors.Ceragon
alias Towerops.Snmp.SnmpMock
setup :verify_on_exit!
@client_opts [
ip: "192.168.1.1",
community: "public",
version: "2c",
port: 161,
timeout: 5000
]
describe "profile_names/0" do
test "returns ceraos profile name" do
assert Ceragon.profile_names() == ["ceraos"]
end
end
describe "detect_hardware/1" do
test "detects hardware from model OID" do
expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.2281.1.1.1.0", _ ->
{:ok, "FibeAir IP-20C"}
end)
assert Ceragon.detect_hardware(@client_opts) == "FibeAir IP-20C"
end
test "returns nil when OID unavailable" do
expect(SnmpMock, :get, fn _, _, _ ->
{:error, :no_such_object}
end)
assert Ceragon.detect_hardware(@client_opts) == nil
end
end
describe "wireless_oid_defs/0" do
test "returns list of sensor definitions" do
defs = Ceragon.wireless_oid_defs()
assert is_list(defs)
assert [_ | _] = defs
end
test "includes TX and RX power sensors" do
defs = Ceragon.wireless_oid_defs()
tx = Enum.find(defs, &(&1.sensor_descr == "TX Power"))
rx = Enum.find(defs, &(&1.sensor_descr == "RX Level"))
assert tx
assert tx.sensor_type == "power"
assert rx
assert rx.sensor_type == "rssi"
end
test "includes SNR sensor" do
defs = Ceragon.wireless_oid_defs()
snr = Enum.find(defs, &(&1.sensor_descr == "Signal to Noise Ratio"))
assert snr
assert snr.sensor_type == "snr"
end
end
describe "discover_wireless_sensors/1" do
test "discovers sensors when SNMP responds" do
expect(SnmpMock, :get, 4, fn _, oid, _ ->
cond do
String.contains?(oid, "2281.10.4.1.11.1.1.1") -> {:ok, 200}
String.contains?(oid, "2281.10.4.1.11.1.2.1") -> {:ok, -450}
String.contains?(oid, "2281.10.4.1.11.1.3.1") -> {:ok, 320}
String.contains?(oid, "2281.10.4.1.11.1.4.1") -> {:ok, 1}
true -> {:error, :no_such_object}
end
end)
sensors = Ceragon.discover_wireless_sensors(@client_opts)
assert is_list(sensors)
assert length(sensors) == 4
end
test "returns empty list when no sensors respond" do
expect(SnmpMock, :get, 4, fn _, _, _ ->
{:error, :no_such_object}
end)
sensors = Ceragon.discover_wireless_sensors(@client_opts)
assert sensors == []
end
end
end

View file

@ -0,0 +1,99 @@
defmodule Towerops.Snmp.Profiles.Vendors.CmmTest do
use Towerops.DataCase, async: true
import Mox
alias Towerops.Snmp.Profiles.Vendors.Cmm
alias Towerops.Snmp.SnmpMock
setup :verify_on_exit!
@client_opts [
ip: "192.168.1.1",
community: "public",
version: "2c",
port: 161,
timeout: 5000
]
describe "profile_names/0" do
test "returns cmm and cmm4 profile names" do
assert Cmm.profile_names() == ["cmm", "cmm4"]
end
end
describe "detect_hardware/1" do
test "detects hardware from model OID" do
expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.161.19.3.4.4.2.0", _ ->
{:ok, "CMM4"}
end)
assert Cmm.detect_hardware(@client_opts) == "CMM4"
end
test "returns nil when OID unavailable" do
expect(SnmpMock, :get, fn _, _, _ ->
{:error, :no_such_object}
end)
assert Cmm.detect_hardware(@client_opts) == nil
end
end
describe "wireless_oid_defs/0" do
test "returns list of sensor definitions" do
defs = Cmm.wireless_oid_defs()
assert is_list(defs)
assert [_ | _] = defs
end
test "includes power and GPS status sensors" do
defs = Cmm.wireless_oid_defs()
power = Enum.find(defs, &(&1.sensor_descr == "Power Status"))
gps = Enum.find(defs, &(&1.sensor_descr == "GPS Sync Status"))
assert power
assert power.sensor_type == "state"
assert gps
assert gps.sensor_type == "state"
end
test "includes connected APs sensor" do
defs = Cmm.wireless_oid_defs()
aps = Enum.find(defs, &(&1.sensor_descr == "Connected APs"))
assert aps
assert aps.sensor_type == "ap-count"
end
end
describe "discover_wireless_sensors/1" do
test "discovers sensors when SNMP responds" do
expect(SnmpMock, :get, 4, fn _, oid, _ ->
cond do
String.contains?(oid, "161.19.3.4.4.1.0") -> {:ok, 1}
String.contains?(oid, "161.19.3.4.4.3.0") -> {:ok, 1}
String.contains?(oid, "161.19.3.4.4.4.0") -> {:ok, 86_400}
String.contains?(oid, "161.19.3.4.4.5.0") -> {:ok, 8}
true -> {:error, :no_such_object}
end
end)
sensors = Cmm.discover_wireless_sensors(@client_opts)
assert is_list(sensors)
assert length(sensors) == 4
end
test "returns empty list when no sensors respond" do
expect(SnmpMock, :get, 4, fn _, _, _ ->
{:error, :no_such_object}
end)
sensors = Cmm.discover_wireless_sensors(@client_opts)
assert sensors == []
end
end
end

View file

@ -0,0 +1,99 @@
defmodule Towerops.Snmp.Profiles.Vendors.PepwaveTest do
use Towerops.DataCase, async: true
import Mox
alias Towerops.Snmp.Profiles.Vendors.Pepwave
alias Towerops.Snmp.SnmpMock
setup :verify_on_exit!
@client_opts [
ip: "192.168.1.1",
community: "public",
version: "2c",
port: 161,
timeout: 5000
]
describe "profile_names/0" do
test "returns pepwave profile name" do
assert Pepwave.profile_names() == ["pepwave"]
end
end
describe "detect_hardware/1" do
test "detects hardware from model OID" do
expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.27662.1.1.1.0", _ ->
{:ok, "MAX HD4"}
end)
assert Pepwave.detect_hardware(@client_opts) == "MAX HD4"
end
test "returns nil when OID unavailable" do
expect(SnmpMock, :get, fn _, _, _ ->
{:error, :no_such_object}
end)
assert Pepwave.detect_hardware(@client_opts) == nil
end
end
describe "wireless_oid_defs/0" do
test "returns list of sensor definitions" do
defs = Pepwave.wireless_oid_defs()
assert is_list(defs)
assert [_ | _] = defs
end
test "includes signal strength sensor" do
defs = Pepwave.wireless_oid_defs()
signal = Enum.find(defs, &(&1.sensor_descr == "WAN1 Signal Strength"))
assert signal
assert signal.sensor_type == "rssi"
end
test "includes CPU and memory sensors" do
defs = Pepwave.wireless_oid_defs()
cpu = Enum.find(defs, &(&1.sensor_descr == "CPU Utilization"))
mem = Enum.find(defs, &(&1.sensor_descr == "Memory Utilization"))
assert cpu
assert cpu.sensor_type == "load"
assert mem
assert mem.sensor_type == "load"
end
end
describe "discover_wireless_sensors/1" do
test "discovers sensors when SNMP responds" do
expect(SnmpMock, :get, 4, fn _, oid, _ ->
cond do
String.contains?(oid, "27662.3.1.1.1.0") -> {:ok, -65}
String.contains?(oid, "27662.3.1.2.1.0") -> {:ok, 1}
String.contains?(oid, "27662.2.1.1.0") -> {:ok, 25}
String.contains?(oid, "27662.2.1.2.0") -> {:ok, 60}
true -> {:error, :no_such_object}
end
end)
sensors = Pepwave.discover_wireless_sensors(@client_opts)
assert is_list(sensors)
assert length(sensors) == 4
end
test "returns empty list when no sensors respond" do
expect(SnmpMock, :get, 4, fn _, _, _ ->
{:error, :no_such_object}
end)
sensors = Pepwave.discover_wireless_sensors(@client_opts)
assert sensors == []
end
end
end

View file

@ -7,6 +7,8 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
alias Towerops.Snmp.Profiles.Vendors.Airos
alias Towerops.Snmp.Profiles.Vendors.Altalabs
alias Towerops.Snmp.Profiles.Vendors.Alvarion
alias Towerops.Snmp.Profiles.Vendors.Ceragon
alias Towerops.Snmp.Profiles.Vendors.Cmm
alias Towerops.Snmp.Profiles.Vendors.Cnpilot
alias Towerops.Snmp.Profiles.Vendors.Deliberant
alias Towerops.Snmp.Profiles.Vendors.Dlinkap
@ -17,6 +19,7 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
alias Towerops.Snmp.Profiles.Vendors.Ligoos
alias Towerops.Snmp.Profiles.Vendors.Mimosa
alias Towerops.Snmp.Profiles.Vendors.MoxaAwk
alias Towerops.Snmp.Profiles.Vendors.Pepwave
alias Towerops.Snmp.Profiles.Vendors.Pmp
alias Towerops.Snmp.Profiles.Vendors.Proxim
alias Towerops.Snmp.Profiles.Vendors.Racom
@ -28,6 +31,7 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
alias Towerops.Snmp.Profiles.Vendors.Siae
alias Towerops.Snmp.Profiles.Vendors.Siklu
alias Towerops.Snmp.Profiles.Vendors.Stellar
alias Towerops.Snmp.Profiles.Vendors.Tranzeo
alias Towerops.Snmp.Profiles.Vendors.Unifi
alias Towerops.Snmp.Profiles.Vendors.Xirrus
alias Towerops.Snmp.Profiles.Vendors.ZyxelWlc
@ -74,6 +78,10 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
assert Dlinkap in vendors
assert Fortiap in vendors
assert MoxaAwk in vendors
assert Ceragon in vendors
assert Cmm in vendors
assert Pepwave in vendors
assert Tranzeo in vendors
end
end
@ -110,6 +118,10 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
assert "dlinkap" in names
assert "fortiap" in names
assert "moxa-awk" in names
assert "ceraos" in names
assert "cmm" in names
assert "pepwave" in names
assert "tranzeo" in names
end
end
@ -222,6 +234,22 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
assert Registry.get_vendor("moxa-awk") == MoxaAwk
end
test "returns Ceragon for ceraos profile" do
assert Registry.get_vendor("ceraos") == Ceragon
end
test "returns Cmm for cmm profile" do
assert Registry.get_vendor("cmm") == Cmm
end
test "returns Pepwave for pepwave profile" do
assert Registry.get_vendor("pepwave") == Pepwave
end
test "returns Tranzeo for tranzeo profile" do
assert Registry.get_vendor("tranzeo") == Tranzeo
end
test "returns nil for unknown profile" do
assert Registry.get_vendor("unknown_vendor") == nil
end

View file

@ -0,0 +1,107 @@
defmodule Towerops.Snmp.Profiles.Vendors.TranzeoTest do
use Towerops.DataCase, async: true
import Mox
alias Towerops.Snmp.Profiles.Vendors.Tranzeo
alias Towerops.Snmp.SnmpMock
setup :verify_on_exit!
@client_opts [
ip: "192.168.1.1",
community: "public",
version: "2c",
port: 161,
timeout: 5000
]
describe "profile_names/0" do
test "returns tranzeo profile name" do
assert Tranzeo.profile_names() == ["tranzeo"]
end
end
describe "detect_hardware/1" do
test "detects hardware from sysDescr containing Tranzeo" do
expect(SnmpMock, :get, fn _, "1.3.6.1.2.1.1.1.0", _ ->
{:ok, "Tranzeo TR-5A Plus"}
end)
assert Tranzeo.detect_hardware(@client_opts) == "TR-5A Plus"
end
test "returns nil when sysDescr does not contain Tranzeo" do
expect(SnmpMock, :get, fn _, _, _ ->
{:ok, "Some Other Device"}
end)
assert Tranzeo.detect_hardware(@client_opts) == nil
end
test "returns nil when OID unavailable" do
expect(SnmpMock, :get, fn _, _, _ ->
{:error, :no_such_object}
end)
assert Tranzeo.detect_hardware(@client_opts) == nil
end
end
describe "wireless_oid_defs/0" do
test "returns list of sensor definitions" do
defs = Tranzeo.wireless_oid_defs()
assert is_list(defs)
assert [_ | _] = defs
end
test "includes associated clients sensor" do
defs = Tranzeo.wireless_oid_defs()
clients = Enum.find(defs, &(&1.sensor_descr == "Associated Clients"))
assert clients
assert clients.sensor_type == "clients"
end
test "includes signal and power sensors" do
defs = Tranzeo.wireless_oid_defs()
signal = Enum.find(defs, &(&1.sensor_descr == "Signal Strength"))
tx_power = Enum.find(defs, &(&1.sensor_descr == "TX Power"))
assert signal
assert signal.sensor_type == "rssi"
assert tx_power
assert tx_power.sensor_type == "power"
end
end
describe "discover_wireless_sensors/1" do
test "discovers sensors when SNMP responds" do
expect(SnmpMock, :get, 4, fn _, oid, _ ->
cond do
String.contains?(oid, "24350.2.1.1.0") -> {:ok, 15}
String.contains?(oid, "24350.2.2.1.0") -> {:ok, -55}
String.contains?(oid, "24350.2.2.2.0") -> {:ok, -95}
String.contains?(oid, "24350.2.2.3.0") -> {:ok, 20}
true -> {:error, :no_such_object}
end
end)
sensors = Tranzeo.discover_wireless_sensors(@client_opts)
assert is_list(sensors)
assert length(sensors) == 4
end
test "returns empty list when no sensors respond" do
expect(SnmpMock, :get, 4, fn _, _, _ ->
{:error, :no_such_object}
end)
sensors = Tranzeo.discover_wireless_sensors(@client_opts)
assert sensors == []
end
end
end