Skip to content

Commit caab499

Browse files
authored
Add support for Grandstream Access Points (GWN76xx) (librenms#18303)
* Add support for Grandstream Access Points (GWN76xx) * Add channel discovery for grandstream-ap * Add TX power discovery for grandstream-ap
1 parent 4a7ca88 commit caab499

File tree

6 files changed

+6661
-0
lines changed

6 files changed

+6661
-0
lines changed

LibreNMS/OS/GrandstreamAp.php

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<?php
2+
3+
namespace LibreNMS\OS;
4+
5+
use App\Models\Device;
6+
use App\Models\Sensor;
7+
use LibreNMS\Device\WirelessSensor;
8+
use LibreNMS\Interfaces\Discovery\OSDiscovery;
9+
use LibreNMS\Interfaces\Discovery\Sensors\WirelessChannelDiscovery;
10+
use LibreNMS\Interfaces\Discovery\Sensors\WirelessClientsDiscovery;
11+
use LibreNMS\Interfaces\Discovery\Sensors\WirelessPowerDiscovery;
12+
use LibreNMS\Interfaces\Polling\Sensors\WirelessClientsPolling;
13+
use LibreNMS\OS;
14+
use SnmpQuery;
15+
16+
class GrandstreamAp extends OS implements
17+
OSDiscovery,
18+
WirelessChannelDiscovery,
19+
WirelessClientsDiscovery,
20+
WirelessClientsPolling,
21+
WirelessPowerDiscovery
22+
{
23+
public function discoverOS(Device $device): void
24+
{
25+
$response = SnmpQuery::get([
26+
'GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnDeviceVersion.0',
27+
'GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnDeviceModel.0',
28+
])->values();
29+
30+
$device->version = $response['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnDeviceVersion.0'] ?: null;
31+
$device->hardware = $response['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnDeviceModel.0'] ?: null;
32+
}
33+
34+
public function discoverWirelessClients(): array
35+
{
36+
$sensors = [];
37+
38+
// Fetch all configured SSIDs
39+
$ssid_data = SnmpQuery::walk('GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnWlanESSID')->table(1);
40+
$unique_ssids = [];
41+
foreach ($ssid_data as $entry) {
42+
$essid = $entry['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnWlanESSID'] ?? null;
43+
if ($essid !== null) {
44+
$unique_ssids[$essid] = true;
45+
}
46+
}
47+
$unique_ssids = array_keys($unique_ssids);
48+
49+
// Fetch all client SSIDs
50+
$client_data = SnmpQuery::walk('GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnClientESSID')->table(0);
51+
52+
// Assemble Sensors
53+
$total_clients = 0;
54+
foreach ($unique_ssids as $ssid_name) {
55+
if (str_starts_with($ssid_name, 'GWN-MESH-')) {
56+
continue;
57+
}
58+
59+
$description = sprintf('SSID %s Clients', $ssid_name);
60+
$client_count = 0;
61+
$client_list = $client_data['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnClientESSID'] ?? null;
62+
if (is_array($client_list) && ! empty($client_list)) {
63+
foreach ($client_data['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnClientESSID'] as $essid) {
64+
if ($essid === $ssid_name) {
65+
$client_count++;
66+
}
67+
}
68+
}
69+
$total_clients += $client_count;
70+
$sensors[] = new WirelessSensor('clients', $this->getDeviceId(), [], 'grandstream-ap', $ssid_name, $description, $client_count);
71+
}
72+
73+
$sensors[] = new WirelessSensor('clients', $this->getDeviceId(), [], 'grandstream-ap', 'total-clients', 'Total Clients', $total_clients);
74+
75+
return $sensors;
76+
}
77+
78+
/**
79+
* Poll wireless clients
80+
* The returned array should be sensor_id => value pairs
81+
*
82+
* @param Sensor[] $sensors Array of sensors needed to be polled
83+
* @return array of polled data
84+
*/
85+
public function pollWirelessClients(array $sensors): array
86+
{
87+
$data = [];
88+
if (! empty($sensors)) {
89+
$clients = SnmpQuery::walk('GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnClientESSID')->values();
90+
$ssid_counts = array_count_values($clients);
91+
$ssid_counts['total-clients'] = count($clients); // insert total-clients for nice logic below
92+
foreach ($sensors as $sensor) {
93+
$data[$sensor->sensor_id] = $ssid_counts[$sensor->sensor_index] ?? 0;
94+
}
95+
}
96+
97+
return $data;
98+
}
99+
100+
public function discoverWirelessChannel(): array
101+
{
102+
$sensors = [];
103+
104+
$carrier = SnmpQuery::cache()->walk('GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnRadioName')->valuesByIndex();
105+
$data = SnmpQuery::walk('GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnRadioChannel')->valuesByIndex($carrier);
106+
107+
foreach ($data as $index => $entry) {
108+
if (isset($entry['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnRadioChannel'])) {
109+
$sensors[] = new WirelessSensor(
110+
'channel',
111+
$this->getDeviceId(),
112+
'.1.3.6.1.4.1.42397.1.1.3.1.1.4.' . $index,
113+
'grandstream-ap',
114+
$index,
115+
'CHANNEL: ' . $entry['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnRadioName'],
116+
$entry['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnRadioChannel']
117+
);
118+
}
119+
}
120+
121+
return $sensors;
122+
}
123+
124+
/**
125+
* Discover wireless tx power. This is in dBm. Type is power.
126+
* Returns an array of LibreNMS\Device\Sensor objects that have been discovered
127+
*
128+
* @return array
129+
*/
130+
public function discoverWirelessPower(): array
131+
{
132+
$sensors = [];
133+
134+
$carrier = SnmpQuery::cache()->walk('GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnRadioName')->table(1);
135+
$data = SnmpQuery::walk('GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnRadioTransmitPower')->valuesByIndex($carrier);
136+
137+
foreach ($data as $index => $entry) {
138+
if (isset($entry['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnRadioTransmitPower'])) {
139+
$sensors[] = new WirelessSensor(
140+
'power',
141+
$this->getDeviceId(),
142+
'.1.3.6.1.4.1.42397.1.1.3.1.1.5.' . $index,
143+
'grandstream-ap',
144+
$index,
145+
'Tx Power: ' . $entry['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnRadioName'],
146+
$entry['GRANDSTREAM-GWN-PRODUCTS-AP-MIB::gwnRadioTransmitPower']
147+
);
148+
}
149+
}
150+
151+
return $sensors;
152+
}
153+
}

0 commit comments

Comments
 (0)