name change and more modules added. config not started.

This commit is contained in:
frosty 2024-08-14 12:28:29 -04:00
parent 1e204a2ab7
commit 886e0c5a97
14 changed files with 486 additions and 35 deletions

2
.gitignore vendored
View file

@ -1 +1 @@
sysinfo modbot

View file

@ -1,3 +1,7 @@
# sysinfo # modbot
sysinfo queries your system for various information and allows you to format it however you'd like. modbot is a seriously over-engineered system for querying different information about your system. Often used for a status bar like [dzen](https://github.com/robm/dzen) or [lemonbar](https://github.com/LemonBoy/bar).
Each part of the output is a module, and modbot is an agregator for these modules. You can query different information about your system like load average, CPU temperature, wireless SSID, and even the output of an arbitrary command or file.
The modules that are in the output are determined at compile time, within the `config.go` file. Don't worry-this process uses a straightforward syntax that doesn't require any programming knowledge. Though, if you're using this, I assume you're smart enough to know that.

2
go.mod
View file

@ -1,4 +1,4 @@
module codeberg.org/frosty/sysinfo module codeberg.org/frosty/modbot
go 1.22.4 go 1.22.4

171
lib/readers/battery.go Normal file
View file

@ -0,0 +1,171 @@
// modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package readers
import (
"bufio"
"fmt"
"os"
"strconv"
)
type BatteryStatus int
type BatteryTechnology int
const (
StatusUnknown BatteryStatus = iota
StatusCharging
StatusDischarging
StatusNotCharging
StatusFull
)
const (
TechnologyUnknown BatteryTechnology = iota
TechnologyNimh
TechnologyLiion
TechnologyLipoly
TechnologyLife
TechnologyNicd
TechnologyLimn
)
type BatteryInfo struct {
Capacity uint8
Status BatteryStatus
Technology BatteryTechnology
}
func (bs BatteryStatus) String() string {
switch bs {
case StatusCharging:
return "Charging"
case StatusDischarging:
return "Discharging"
case StatusNotCharging:
return "Not charging"
case StatusFull:
return "Full"
default:
return "Unknown"
}
}
func (bt BatteryTechnology) String() string {
switch bt {
case TechnologyNimh:
return "NiMH"
case TechnologyLiion:
return "Li-ion"
case TechnologyLipoly:
return "Li-poly"
case TechnologyLife:
return "LiFe"
case TechnologyNicd:
return "NiCd"
case TechnologyLimn:
return "LiMn"
default:
return "Unknown"
}
}
var batteryStatusMap = map[string]BatteryStatus{
"Unknown": StatusUnknown,
"Charging": StatusCharging,
"Discharging": StatusDischarging,
"Not charging": StatusNotCharging,
"Full": StatusFull,
}
var batteryTechnologyMap = map[string]BatteryTechnology{
"Unknown": TechnologyUnknown,
"NiMH": TechnologyNimh,
"Li-ion": TechnologyLiion,
"Li-poly": TechnologyLipoly,
"LiFe": TechnologyLife,
"NiCd": TechnologyNicd,
"LiMn": TechnologyLimn,
}
func BatteryStatusFromStr(statusStr string) BatteryStatus {
if status, exists := batteryStatusMap[statusStr]; exists {
return status
}
return StatusUnknown
}
func BatteryTechnologyFromStr(technologyStr string) BatteryTechnology {
if technology, exists := batteryTechnologyMap[technologyStr]; exists {
return technology
}
return TechnologyUnknown
}
func ReadBattery(batteryName string) (BatteryInfo, error) {
capacityPath := fmt.Sprintf("/sys/class/power_supply/%s/capacity", batteryName)
statusPath := fmt.Sprintf("/sys/class/power_supply/%s/status", batteryName)
technologyPath := fmt.Sprintf("/sys/class/power_supply/%s/technology", batteryName)
capacityFile, err := os.Open(capacityPath)
if err != nil {
return BatteryInfo{}, fmt.Errorf("failed to open %s: %w", capacityPath, err)
}
defer capacityFile.Close()
capacityScanner := bufio.NewScanner(capacityFile)
if !capacityScanner.Scan() {
return BatteryInfo{}, fmt.Errorf("failed to read from %s: %w", capacityPath, capacityScanner.Err())
}
statusFile, err := os.Open(statusPath)
if err != nil {
return BatteryInfo{}, fmt.Errorf("failed to open %s: %w", statusPath, err)
}
defer statusFile.Close()
statusScanner := bufio.NewScanner(statusFile)
if !statusScanner.Scan() {
return BatteryInfo{}, fmt.Errorf("failed to read from %s: %w", statusPath, statusScanner.Err())
}
technologyFile, err := os.Open(technologyPath)
if err != nil {
return BatteryInfo{}, fmt.Errorf("failed to open %s: %w", technologyPath, err)
}
defer technologyFile.Close()
technologyScanner := bufio.NewScanner(technologyFile)
if !technologyScanner.Scan() {
return BatteryInfo{}, fmt.Errorf("failed to read from %s: %w", technologyPath, technologyScanner.Err())
}
batteryCapacityStr := capacityScanner.Text()
batteryStatus := statusScanner.Text()
batteryTechnology := technologyScanner.Text()
batteryCapacity, err := strconv.ParseUint(batteryCapacityStr, 10, 8)
if err != nil {
return BatteryInfo{}, fmt.Errorf("failed to parse capacity from %s: %w", capacityPath, err)
}
return BatteryInfo{
Capacity: uint8(batteryCapacity),
Status: BatteryStatusFromStr(batteryStatus),
Technology: BatteryTechnologyFromStr(batteryTechnology),
}, nil
}

View file

@ -0,0 +1,51 @@
// modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package readers
import (
"bufio"
"fmt"
"os"
"strconv"
)
type CpuTemperatureInfo float32
func ReadCpuTemperature(hwmonName, tempName string) (CpuTemperatureInfo, error) {
tempPath := fmt.Sprintf("/sys/class/hwmon/%s/%s_input", hwmonName, tempName)
file, err := os.Open(tempPath)
if err != nil {
return 0, fmt.Errorf("failed to open %s: %w", tempPath, err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
if !scanner.Scan() {
return 0, fmt.Errorf("failed to read from %s: %w", tempPath, scanner.Err())
}
line := scanner.Text()
cpuTemperatureMdeg, err := strconv.ParseUint(line, 10, 32)
if err != nil {
return 0, fmt.Errorf("failed to parse cpu temperature from %s: %w", tempPath, err)
}
cpuTemperature := float32(cpuTemperatureMdeg) / 1000
return CpuTemperatureInfo(cpuTemperature), nil
}

View file

@ -1,4 +1,4 @@
// sysinfo queries information about your system // modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com> // Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify

17
lib/readers/disk_io.go Normal file
View file

@ -0,0 +1,17 @@
// modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package readers

17
lib/readers/disk_usage.go Normal file
View file

@ -0,0 +1,17 @@
// modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package readers

58
lib/readers/load.go Normal file
View file

@ -0,0 +1,58 @@
// modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package readers
import (
"bufio"
"fmt"
"os"
"strings"
)
type LoadInfo struct {
OneMinute string
FiveMinute string
FifteenMinute string
}
func ReadLoad() (LoadInfo, error) {
const loadPath = "/proc/loadavg"
file, err := os.Open(loadPath)
if err != nil {
return LoadInfo{}, fmt.Errorf("failed to open %s: %w", loadPath, err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
scanner.Scan()
if err := scanner.Err(); err != nil {
return LoadInfo{}, fmt.Errorf("failed to read from %s: %w", loadPath, err)
}
line := scanner.Text()
fields := strings.Fields(line)
if len(fields) < 3 {
return LoadInfo{}, fmt.Errorf("unexpected format in %s: %s", loadPath, line)
}
return LoadInfo{
OneMinute: fields[0],
FiveMinute: fields[1],
FifteenMinute: fields[2],
}, nil
}

View file

@ -1,4 +1,4 @@
// sysinfo queries information about your system // modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com> // Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify

View file

@ -1,4 +1,4 @@
// sysinfo queries information about your system // modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com> // Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
@ -18,6 +18,7 @@ package readers
import ( import (
"bufio" "bufio"
"fmt"
"os" "os"
"strings" "strings"
) )
@ -29,9 +30,11 @@ type OsInfo struct {
} }
func ReadOs() (OsInfo, error) { func ReadOs() (OsInfo, error) {
file, err := os.Open("/lib/os-release") const osPath = "/lib/os-release"
file, err := os.Open(osPath)
if err != nil { if err != nil {
return OsInfo{}, err return OsInfo{}, fmt.Errorf("failed to open %s: %w", osPath, err)
} }
defer file.Close() defer file.Close()
@ -41,18 +44,26 @@ func ReadOs() (OsInfo, error) {
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
line = strings.ReplaceAll(line, "\"", "") line = strings.ReplaceAll(line, "\"", "")
columns := strings.Split(line, "=") fields := strings.SplitN(line, "=", 2)
if len(fields) < 2 {
return OsInfo{}, fmt.Errorf("unexpected format in %s: %s", osPath, line)
}
switch { switch {
case strings.HasPrefix(line, "NAME="): case strings.HasPrefix(line, "NAME"):
osName = columns[1] osName = fields[1]
case strings.HasPrefix(line, "PRETTY_NAME="): case strings.HasPrefix(line, "PRETTY_NAME"):
osPrettyName = columns[1] osPrettyName = fields[1]
case strings.HasPrefix(line, "VERSION_ID="): case strings.HasPrefix(line, "VERSION_ID"):
osVersion = columns[1] osVersion = fields[1]
} }
} }
if err := scanner.Err(); err != nil {
return OsInfo{}, fmt.Errorf("failed to read from %s: %w", osPath, err)
}
return OsInfo{ return OsInfo{
Name: osName, Name: osName,
PrettyName: osPrettyName, PrettyName: osPrettyName,

94
lib/readers/uptime.go Normal file
View file

@ -0,0 +1,94 @@
// modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package readers
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
const (
SecondsInMinute = 60
SecondsInHour = 3600
SecondsInDay = 86400
)
type UptimeInfo uint64
func (u UptimeInfo) Days() int {
return int(u) / SecondsInDay
}
func (u UptimeInfo) Hours() int {
return (int(u) % SecondsInDay) / SecondsInHour
}
func (u UptimeInfo) Minutes() int {
return (int(u) % SecondsInHour) / SecondsInMinute
}
func (u UptimeInfo) Seconds() int {
return int(u) % SecondsInMinute
}
func (u UptimeInfo) String() string {
var builder strings.Builder
if u.Days() > 0 {
builder.WriteString(fmt.Sprintf("%d days, ", u.Days()))
}
if u.Hours() > 0 {
builder.WriteString(fmt.Sprintf("%d hours, ", u.Hours()))
}
if u.Minutes() > 0 {
builder.WriteString(fmt.Sprintf("%d minutes, ", u.Minutes()))
}
builder.WriteString(fmt.Sprintf("%d seconds", u.Seconds()))
return builder.String()
}
func ReadUptime() (UptimeInfo, error) {
const uptimePath = "/proc/uptime"
file, err := os.Open(uptimePath)
if err != nil {
return 0, fmt.Errorf("failed to open %s: %w", uptimePath, err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
if !scanner.Scan() {
return 0, fmt.Errorf("failed to read from %s: %w", uptimePath, scanner.Err())
}
line := scanner.Text()
fields := strings.SplitN(line, ".", 2)
if len(fields) < 1 {
return 0, fmt.Errorf("unexpected format in %s: %s", uptimePath, line)
}
uptime, err := strconv.ParseUint(fields[0], 10, 64)
if err != nil {
return 0, fmt.Errorf("failed to parse uptime from %s: %w", uptimePath, err)
}
return UptimeInfo(uptime), nil
}

View file

@ -1,4 +1,4 @@
// sysinfo queries information about your system // modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com> // Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
@ -19,26 +19,29 @@ package ui
import "fmt" import "fmt"
const ( const (
kibibyte = 1 Kibibyte = 1
mebibyte = 1024 * kibibyte Mebibyte = 1024 * Kibibyte
gibibyte = 1024 * mebibyte Gibibyte = 1024 * Mebibyte
Tebibyte = 1024 * Gibibyte
) )
func PrettifySize(sizeKibibytes uint64, decimalPlaces uint8) string { func PrettifyKib(sizeKibibytes uint64, decimalPlaces uint8) string {
var size float64 var size float64
var unit string var unit string
if sizeKibibytes < mebibyte { if sizeKibibytes < Mebibyte {
size = float64(sizeKibibytes) size = float64(sizeKibibytes)
unit = "KiB" unit = "KiB"
} else if sizeKibibytes < gibibyte { } else if sizeKibibytes < Gibibyte {
size = float64(sizeKibibytes) / float64(mebibyte) size = float64(sizeKibibytes) / float64(Mebibyte)
unit = "MiB" unit = "MiB"
} else { } else if sizeKibibytes < Tebibyte {
size = float64(sizeKibibytes) / float64(gibibyte) size = float64(sizeKibibytes) / float64(Gibibyte)
unit = "GiB" unit = "GiB"
} else {
size = float64(sizeKibibytes) / float64(Tebibyte)
unit = "TiB"
} }
format := fmt.Sprintf("%%.%df %s", decimalPlaces, unit) return fmt.Sprintf(fmt.Sprintf("%%.%df %s", decimalPlaces, unit), size)
return fmt.Sprintf(format, size)
} }

39
main.go
View file

@ -1,4 +1,4 @@
// sysinfo queries information about your system // modbot is a system information agregator
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com> // Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
@ -20,13 +20,16 @@ import (
"fmt" "fmt"
"log" "log"
"codeberg.org/frosty/sysinfo/lib/readers" "codeberg.org/frosty/modbot/lib/readers"
"codeberg.org/frosty/sysinfo/lib/ui" "codeberg.org/frosty/modbot/lib/ui"
) )
const ( const (
memoryDecimalPlaces = 2 MemoryDecimalPlaces = 2
cpuUsageDecimalPlaces = 2 CpuUsageDecimalPlaces = 2
CpuTemperatureHwmonName = "hwmon6"
CpuTemperatureTempName = "temp1"
BatteryName = "BAT1"
) )
func main() { func main() {
@ -40,11 +43,33 @@ func main() {
if err != nil { if err != nil {
log.Fatalf("%v\n", err) log.Fatalf("%v\n", err)
} }
fmt.Printf("RAM: %v / %v\n", ui.PrettifySize(memoryInfo.Used, memoryDecimalPlaces), ui.PrettifySize(memoryInfo.Total, memoryDecimalPlaces)) fmt.Printf("RAM: %v / %v\n", ui.PrettifyKib(memoryInfo.Used, MemoryDecimalPlaces), ui.PrettifyKib(memoryInfo.Total, MemoryDecimalPlaces))
cpuUsageInfo, err := readers.ReadCpuUsage() cpuUsageInfo, err := readers.ReadCpuUsage()
if err != nil { if err != nil {
log.Fatalf("%v\n", err) log.Fatalf("%v\n", err)
} }
fmt.Printf("CPU: %.*f%%\n", cpuUsageDecimalPlaces, cpuUsageInfo.UsagePercent) cpuTemperatureInfo, err := readers.ReadCpuTemperature(CpuTemperatureHwmonName, CpuTemperatureTempName)
if err != nil {
log.Fatalf("%v\n", err)
}
fmt.Printf("CPU: %.1f°C (%.*f%%)\n", cpuTemperatureInfo, CpuUsageDecimalPlaces, cpuUsageInfo.UsagePercent)
loadInfo, err := readers.ReadLoad()
if err != nil {
log.Fatalf("%v\n", err)
}
fmt.Printf("Load: %v %v %v\n", loadInfo.OneMinute, loadInfo.FiveMinute, loadInfo.FifteenMinute)
// batteryInfo, err := readers.ReadBattery(BatteryName)
// if err != nil {
// log.Fatalf("%v\n", err)
// }
// fmt.Printf("Battery: %v%% (%v) w/ %v\n", batteryInfo.Capacity, batteryInfo.Status, batteryInfo.Technology)
uptimeInfo, err := readers.ReadUptime()
if err != nil {
log.Fatalf("%v\n", err)
}
fmt.Printf("Uptime: %v\n", uptimeInfo)
} }