mirror of
https://github.com/henrygd/beszel.git
synced 2026-09-27 03:47:47 +02:00
store compact RSSI stats and skip real-time collection
Stats.WiFi is now map[string]int8 (json "wf") holding only available RSSI readings; SSID and unavailable signals stay in Info.WiFi. Averages are rounded to whole dBm. Wi-Fi is collected only on the default 60s interval; real-time requests reuse the last snapshot to avoid spawning osascript / dumping the BSS cache every second.
This commit is contained in:
@@ -20,7 +20,6 @@ type WiFi struct {
|
||||
}
|
||||
|
||||
type Stats struct {
|
||||
WiFi map[string]WiFi `json:"wifi,omitempty" cbor:"40,keyasint,omitempty"`
|
||||
Cpu float64 `json:"cpu" cbor:"0,keyasint"`
|
||||
MaxCpu float64 `json:"cpum,omitempty" cbor:"-"`
|
||||
Mem float64 `json:"m" cbor:"2,keyasint"`
|
||||
@@ -64,6 +63,7 @@ type Stats struct {
|
||||
Batteries map[string]uint8 `json:"bats,omitempty" cbor:"37,keyasint,omitempty"`
|
||||
ZfsPools map[string]*ZfsPool `json:"z,omitempty" cbor:"39,keyasint,omitempty"` // ZFS pool metrics, keyed by pool name
|
||||
DiskIOTotal [2]uint64 `json:"diot,omitzero" cbor:"38,keyasint,omitzero"` // [total read bytes, total write bytes] cumulative device counters
|
||||
WiFi map[string]int8 `json:"wf,omitempty" cbor:"40,keyasint,omitempty"` // RSSI dBm keyed by interface; unavailable readings omitted
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@ import (
|
||||
func TestWiFiWireSnapshot(t *testing.T) {
|
||||
signal := -55.0
|
||||
for _, wifi := range []map[string]WiFi{nil, {}, {"wlan0": {SSID: "home", Signal: &signal}, "wlan1": {}}} {
|
||||
original := CombinedData{Info: Info{WiFi: wifi}, Stats: Stats{WiFi: wifi}}
|
||||
original := CombinedData{Info: Info{WiFi: wifi}, Stats: Stats{WiFi: make(map[string]int8, len(wifi))}}
|
||||
for id := range wifi {
|
||||
original.Stats.WiFi[id] = -55
|
||||
}
|
||||
encoded, err := cbor.Marshal(original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestCreateRecordsWiFiDisconnectReconnect(t *testing.T) {
|
||||
{}, nil,
|
||||
{"wlan0": {SSID: "new", Signal: &signal}},
|
||||
} {
|
||||
_, err := sys.createRecords(&system.CombinedData{Info: system.Info{WiFi: snapshot}, Stats: system.Stats{WiFi: snapshot}})
|
||||
_, err := sys.createRecords(&system.CombinedData{Info: system.Info{WiFi: snapshot}})
|
||||
require.NoError(t, err)
|
||||
record, err := app.FindRecordById("systems", sys.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -16,13 +16,21 @@ func TestWiFiSequentialResponseSnapshots(t *testing.T) {
|
||||
{"wlan0": {SSID: "home"}}, {}, nil,
|
||||
{"wlan1": {SSID: "new", Signal: &signal}},
|
||||
} {
|
||||
payload, err := cbor.Marshal(system.CombinedData{Info: system.Info{WiFi: snapshot}, Stats: system.Stats{WiFi: snapshot}})
|
||||
signals := make(map[string]int8)
|
||||
for id, reading := range snapshot {
|
||||
if reading.Signal != nil {
|
||||
signals[id] = int8(*reading.Signal)
|
||||
}
|
||||
}
|
||||
payload, err := cbor.Marshal(system.CombinedData{Info: system.Info{WiFi: snapshot}, Stats: system.Stats{WiFi: signals}})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, UnmarshalResponse(common.AgentResponse{Data: payload}, common.GetData, &decoded))
|
||||
require.Len(t, decoded.Info.WiFi, len(snapshot))
|
||||
require.Len(t, decoded.Stats.WiFi, len(snapshot))
|
||||
require.Len(t, decoded.Stats.WiFi, len(signals))
|
||||
for id, want := range snapshot {
|
||||
require.Equal(t, want, decoded.Info.WiFi[id])
|
||||
}
|
||||
for id, want := range signals {
|
||||
require.Equal(t, want, decoded.Stats.WiFi[id])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,8 +267,7 @@ func AverageSystemStatsSlice(records []system.Stats) system.Stats {
|
||||
return sum
|
||||
}
|
||||
|
||||
// RSSI averages exclude absent and unavailable samples.
|
||||
wifiSums := make(map[string]float64)
|
||||
wifiSums := make(map[string]int)
|
||||
wifiCounts := make(map[string]int)
|
||||
// necessary because uint8 is not big enough for the sum
|
||||
batterySum := 0
|
||||
@@ -288,15 +287,9 @@ func AverageSystemStatsSlice(records []system.Stats) system.Stats {
|
||||
// Accumulate totals
|
||||
for i := range records {
|
||||
stats := &records[i]
|
||||
for id, reading := range stats.WiFi {
|
||||
if sum.WiFi == nil {
|
||||
sum.WiFi = make(map[string]system.WiFi)
|
||||
}
|
||||
sum.WiFi[id] = system.WiFi{SSID: reading.SSID}
|
||||
if reading.Signal != nil {
|
||||
wifiSums[id] += *reading.Signal
|
||||
wifiCounts[id]++
|
||||
}
|
||||
for id, signal := range stats.WiFi {
|
||||
wifiSums[id] += int(signal)
|
||||
wifiCounts[id]++
|
||||
}
|
||||
|
||||
sum.Cpu += stats.Cpu
|
||||
@@ -627,11 +620,11 @@ func AverageSystemStatsSlice(records []system.Stats) system.Stats {
|
||||
sum.CpuBreakdown = avg
|
||||
}
|
||||
|
||||
for id, reading := range sum.WiFi {
|
||||
if wifiCounts[id] > 0 {
|
||||
average := wifiSums[id] / float64(wifiCounts[id])
|
||||
reading.Signal = &average
|
||||
sum.WiFi[id] = reading
|
||||
// RSSI averages exclude records where the interface was absent.
|
||||
if len(wifiSums) > 0 {
|
||||
sum.WiFi = make(map[string]int8, len(wifiSums))
|
||||
for id, total := range wifiSums {
|
||||
sum.WiFi[id] = int8(math.Round(float64(total) / float64(wifiCounts[id])))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,17 +7,16 @@ import (
|
||||
)
|
||||
|
||||
func TestWiFiAverageAvailableSamples(t *testing.T) {
|
||||
a, b, c := -40.0, -60.0, -80.0
|
||||
input := []system.Stats{
|
||||
{WiFi: map[string]system.WiFi{"wlan0": {SSID: "old", Signal: &a}}},
|
||||
{WiFi: map[string]int8{"wlan0": -40}},
|
||||
{},
|
||||
{WiFi: map[string]system.WiFi{"wlan0": {SSID: "new", Signal: &b}, "wlan1": {Signal: &c}, "unknown": {}}},
|
||||
{WiFi: map[string]int8{"wlan0": -61, "wlan1": -80}},
|
||||
}
|
||||
result := AverageSystemStatsSlice(input)
|
||||
if len(result.WiFi) != 3 || *result.WiFi["wlan0"].Signal != -50 || *result.WiFi["wlan1"].Signal != -80 || result.WiFi["unknown"].Signal != nil || result.WiFi["wlan0"].SSID != "new" {
|
||||
if len(result.WiFi) != 2 || result.WiFi["wlan0"] != -51 || result.WiFi["wlan1"] != -80 {
|
||||
t.Fatalf("%#v", result.WiFi)
|
||||
}
|
||||
if *input[0].WiFi["wlan0"].Signal != -40 {
|
||||
if input[0].WiFi["wlan0"] != -40 {
|
||||
t.Fatal("mutated input")
|
||||
}
|
||||
if len(AverageSystemStatsSlice([]system.Stats{{}, {}}).WiFi) != 0 {
|
||||
|
||||
@@ -20,7 +20,7 @@ export function WiFiChart({
|
||||
const dataPoints = interfaces.map(([id, current]) => ({
|
||||
label: current.s ? `${id} (${current.s})` : id,
|
||||
color: wifiColor(id),
|
||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.wifi?.[id]?.r,
|
||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.wf?.[id],
|
||||
}))
|
||||
return (
|
||||
<ChartCard
|
||||
|
||||
3
internal/site/src/types.d.ts
vendored
3
internal/site/src/types.d.ts
vendored
@@ -91,7 +91,6 @@ export interface SystemInfo {
|
||||
}
|
||||
|
||||
export interface SystemStats {
|
||||
wifi?: Record<string, WiFi>
|
||||
/** cpu percent */
|
||||
cpu: number
|
||||
/** peak cpu */
|
||||
@@ -168,6 +167,8 @@ export interface SystemStats {
|
||||
bat?: [number, BatteryState]
|
||||
/** battery percentages by device name */
|
||||
bats?: Record<string, number>
|
||||
/** Wi-Fi RSSI (dBm) by interface */
|
||||
wf?: Record<string, number>
|
||||
/** network interfaces [upload bytes, download bytes, total upload bytes, total download bytes] */
|
||||
ni?: Record<string, [number, number, number, number]>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user