Zone-based firewall page, per-sensor temperatures, 15 minute default
The firewall page now manages zones (assign each LAN or tunnel, add and remove zones) and rules with zone, address and port on both sides plus the protocol. router.firewall.zones declares zones without members and rules take sourcePorts. The temperature chart draws every thermal zone and hwmon sensor; the overview defaults to 15 minutes, 1h stays an option. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
This commit is contained in:
@@ -70,7 +70,8 @@ type SystemStatus struct {
|
||||
CPU float64 // percent; NaN until the second sample
|
||||
MemUsed uint64
|
||||
MemTotal uint64
|
||||
Temperature float64 // °C, NaN if no sensor
|
||||
Temperature float64 // °C, the hottest sensor; NaN if there is none
|
||||
Sensors []Sensor
|
||||
DiskUsed uint64
|
||||
DiskTotal uint64
|
||||
}
|
||||
@@ -166,6 +167,9 @@ func (c *Collector) sample(now time.Time) {
|
||||
c.store.Add("sys:mem", now, 100*float64(sys.MemUsed)/float64(sys.MemTotal))
|
||||
}
|
||||
c.store.Add("sys:temp", now, sys.Temperature)
|
||||
for _, t := range sys.Sensors {
|
||||
c.store.Add("temp:"+t.Key, now, t.Temp)
|
||||
}
|
||||
|
||||
up := uplinkStatus(f.WAN)
|
||||
<-done
|
||||
@@ -241,7 +245,8 @@ func cpuTimes() (busy, total uint64) {
|
||||
}
|
||||
|
||||
func systemStatus() SystemStatus {
|
||||
s := SystemStatus{Temperature: temperature()}
|
||||
s := SystemStatus{Sensors: sensors()}
|
||||
s.Temperature = hottest(s.Sensors)
|
||||
if data, err := os.ReadFile("/proc/uptime"); err == nil {
|
||||
if f := strings.Fields(string(data)); len(f) > 0 {
|
||||
secs, _ := strconv.ParseFloat(f[0], 64)
|
||||
@@ -277,23 +282,73 @@ func systemStatus() SystemStatus {
|
||||
return s
|
||||
}
|
||||
|
||||
// temperature is the hottest thermal zone, or hwmon sensor if there is none.
|
||||
func temperature() float64 {
|
||||
hottest := math.NaN()
|
||||
for _, pattern := range []string{"/sys/class/thermal/thermal_zone*/temp", "/sys/class/hwmon/hwmon*/temp*_input"} {
|
||||
files, _ := filepath.Glob(pattern)
|
||||
for _, f := range files {
|
||||
if v, err := readUint(f); err == nil && v > 0 && v < 150000 {
|
||||
if t := float64(v) / 1000; math.IsNaN(hottest) || t > hottest {
|
||||
hottest = t
|
||||
}
|
||||
}
|
||||
}
|
||||
if !math.IsNaN(hottest) {
|
||||
break
|
||||
// Sensor is one temperature the machine reports.
|
||||
type Sensor struct {
|
||||
Key string // stable name of its series
|
||||
Label string
|
||||
Temp float64 // °C
|
||||
}
|
||||
|
||||
// sensors lists the thermal zones and hwmon temperature inputs, sorted by
|
||||
// label. Two sensors with the same name get a number.
|
||||
func sensors() []Sensor {
|
||||
return readSensors("/sys/class/thermal", "/sys/class/hwmon")
|
||||
}
|
||||
|
||||
func readSensors(thermalDir, hwmonDir string) []Sensor {
|
||||
var out []Sensor
|
||||
add := func(label, file string) {
|
||||
if v, err := readUint(file); err == nil && v > 0 && v < 150000 {
|
||||
out = append(out, Sensor{Label: label, Temp: float64(v) / 1000})
|
||||
}
|
||||
}
|
||||
return hottest
|
||||
text := func(file string) string {
|
||||
data, _ := os.ReadFile(file)
|
||||
return strings.TrimSpace(string(data))
|
||||
}
|
||||
zones, _ := filepath.Glob(filepath.Join(thermalDir, "thermal_zone*"))
|
||||
for _, z := range zones {
|
||||
label := text(filepath.Join(z, "type"))
|
||||
if label == "" {
|
||||
label = filepath.Base(z)
|
||||
}
|
||||
add(label, filepath.Join(z, "temp"))
|
||||
}
|
||||
inputs, _ := filepath.Glob(filepath.Join(hwmonDir, "hwmon*", "temp*_input"))
|
||||
for _, in := range inputs {
|
||||
dir, base := filepath.Split(in)
|
||||
chip := text(filepath.Join(dir, "name"))
|
||||
if chip == "" {
|
||||
chip = filepath.Base(dir)
|
||||
}
|
||||
name := strings.TrimSuffix(base, "_input")
|
||||
sub := text(filepath.Join(dir, name+"_label"))
|
||||
if sub == "" {
|
||||
sub = name
|
||||
}
|
||||
add(chip+" "+sub, in)
|
||||
}
|
||||
slices.SortStableFunc(out, func(a, b Sensor) int { return strings.Compare(a.Label, b.Label) })
|
||||
count := map[string]int{}
|
||||
for i := range out {
|
||||
base := out[i].Label
|
||||
count[base]++
|
||||
if n := count[base]; n > 1 {
|
||||
out[i].Label = fmt.Sprintf("%s %d", base, n)
|
||||
}
|
||||
out[i].Key = out[i].Label
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func hottest(sensors []Sensor) float64 {
|
||||
h := math.NaN()
|
||||
for _, t := range sensors {
|
||||
if math.IsNaN(h) || t.Temp > h {
|
||||
h = t.Temp
|
||||
}
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// uplinkDevice is where the router's IPv4 internet traffic leaves.
|
||||
|
||||
Reference in New Issue
Block a user