aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorBlaster4385 <venkatesh@tablaster.dev>2025-01-13 14:29:08 +0530
committerBlaster4385 <venkatesh@tablaster.dev>2025-01-13 16:48:16 +0530
commitc068b6192ae1996a3c09fd73dfdee8adb36b43cc (patch)
tree6f311f6aba7039db8f80c2697def7ac377a9a9db /modules
parentc9b29354af7819c294862b8942c28169a89778a8 (diff)
feat: added support for displaying cpu temperature and usage
Diffstat (limited to 'modules')
-rw-r--r--modules/cpu.go159
-rw-r--r--modules/numbers.go150
2 files changed, 309 insertions, 0 deletions
diff --git a/modules/cpu.go b/modules/cpu.go
new file mode 100644
index 0000000..45f6e46
--- /dev/null
+++ b/modules/cpu.go
@@ -0,0 +1,159 @@
+package modules
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const (
+ tempSensorCacheDuration = 24 * time.Hour
+ cpuUsageSampleInterval = 200 * time.Millisecond
+ tempMilliCelsiusDivisor = 1000.0
+ fahrenheitConversion = 9.0 / 5.0
+ fahrenheitBase = 32.0
+)
+
+type CPUUsage struct {
+ User int64
+ Nice int64
+ System int64
+ Idle int64
+ IOWait int64
+ IRQ int64
+ SoftIRQ int64
+ Steal int64
+}
+
+var (
+ cachedTemp float64
+ lastTempUpdate time.Time
+ cachedTempSensor string
+ tempSensorCachedAt time.Time
+)
+
+func GetCPUTemperature(fahrenheit bool) (float64, error) {
+ now := time.Now()
+ if now.Sub(lastTempUpdate) < time.Second {
+ return cachedTemp, nil
+ }
+
+ tempSensorPath, err := findTempSensor()
+ if err != nil {
+ return 0, fmt.Errorf("finding temp sensor: %w", err)
+ }
+
+ data, err := ioutil.ReadFile(tempSensorPath)
+ if err != nil {
+ return 0, fmt.Errorf("reading CPU temperature (%s): %w", tempSensorPath, err)
+ }
+
+ tempMilliCelsius, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64)
+ if err != nil {
+ return 0, fmt.Errorf("parsing CPU temperature: %w", err)
+ }
+
+ tempCelsius := float64(tempMilliCelsius) / tempMilliCelsiusDivisor
+ if fahrenheit {
+ tempCelsius = (tempCelsius * fahrenheitConversion) + fahrenheitBase
+ }
+
+ cachedTemp = tempCelsius
+ lastTempUpdate = now
+ return tempCelsius, nil
+}
+
+func GetCPUUsage() (float64, error) {
+ prevUsage, err := readCPUUsage()
+ if err != nil {
+ return 0, fmt.Errorf("reading initial CPU usage: %w", err)
+ }
+
+ time.Sleep(cpuUsageSampleInterval)
+
+ currUsage, err := readCPUUsage()
+ if err != nil {
+ return 0, fmt.Errorf("reading current CPU usage: %w", err)
+ }
+
+ usage := calculateCPUUsage(prevUsage, currUsage)
+ return usage, nil
+}
+
+func findTempSensor() (string, error) {
+ if cachedTempSensor != "" && time.Since(tempSensorCachedAt) < tempSensorCacheDuration {
+ return cachedTempSensor, nil
+ }
+
+ hwmonPath := "/sys/class/hwmon"
+ files, err := ioutil.ReadDir(hwmonPath)
+ if err != nil {
+ return "", fmt.Errorf("locating CPU temperature sensor directory: %w", err)
+ }
+
+ for _, file := range files {
+ sensorPath := fmt.Sprintf("%s/%s", hwmonPath, file.Name())
+ nameFilePath := fmt.Sprintf("%s/name", sensorPath)
+ nameData, err := ioutil.ReadFile(nameFilePath)
+ if err != nil {
+ continue
+ }
+ name := strings.TrimSpace(string(nameData))
+ if name == "coretemp" || name == "k10temp" || name == "zenpower" {
+ cachedTempSensor = fmt.Sprintf("%s/temp1_input", sensorPath)
+ tempSensorCachedAt = time.Now()
+ return cachedTempSensor, nil
+ }
+ }
+
+ return "", errors.New("appropriate CPU temperature sensor not found")
+}
+
+func readCPUUsage() (CPUUsage, error) {
+ data, err := os.ReadFile("/proc/stat")
+ if err != nil {
+ return CPUUsage{}, fmt.Errorf("reading /proc/stat: %w", err)
+ }
+ lines := strings.Split(string(data), "\n")
+
+ for _, line := range lines {
+ fields := strings.Fields(line)
+ if fields[0] == "cpu" {
+ usage := CPUUsage{
+ User: parseInt64(fields[1]),
+ Nice: parseInt64(fields[2]),
+ System: parseInt64(fields[3]),
+ Idle: parseInt64(fields[4]),
+ IOWait: parseInt64(fields[5]),
+ IRQ: parseInt64(fields[6]),
+ SoftIRQ: parseInt64(fields[7]),
+ }
+ return usage, nil
+ }
+ }
+
+ return CPUUsage{}, errors.New("failed to parse CPU usage from /proc/stat")
+}
+
+func calculateCPUUsage(prev, curr CPUUsage) float64 {
+ prevTotal := prev.User + prev.Nice + prev.System + prev.Idle + prev.IOWait + prev.IRQ + prev.SoftIRQ
+ currTotal := curr.User + curr.Nice + curr.System + curr.Idle + curr.IOWait + curr.IRQ + curr.SoftIRQ
+
+ totalDiff := float64(currTotal - prevTotal)
+ idleDiff := float64(curr.Idle - prev.Idle)
+
+ if totalDiff == 0 {
+ return 0
+ }
+
+ return (totalDiff - idleDiff) / totalDiff * 100
+}
+
+func parseInt64(s string) int64 {
+ n, _ := strconv.ParseInt(s, 10, 64)
+ return n
+}
diff --git a/modules/numbers.go b/modules/numbers.go
new file mode 100644
index 0000000..2d19b9f
--- /dev/null
+++ b/modules/numbers.go
@@ -0,0 +1,150 @@
+package modules
+
+import (
+ "errors"
+)
+
+type Pattern [][]bool
+
+var DigitPatterns = map[int]Pattern{
+ 0: {
+ {true, true, true},
+ {true, false, true},
+ {true, false, true},
+ {true, false, true},
+ {true, true, true},
+ },
+ 1: {
+ {false, true, false},
+ {true, true, false},
+ {false, true, false},
+ {false, true, false},
+ {true, true, true},
+ },
+ 2: {
+ {true, true, true},
+ {false, false, true},
+ {true, true, true},
+ {true, false, false},
+ {true, true, true},
+ },
+ 3: {
+ {true, true, true},
+ {false, false, true},
+ {true, true, true},
+ {false, false, true},
+ {true, true, true},
+ },
+ 4: {
+ {true, false, true},
+ {true, false, true},
+ {true, true, true},
+ {false, false, true},
+ {false, false, true},
+ },
+ 5: {
+ {true, true, true},
+ {true, false, false},
+ {true, true, true},
+ {false, false, true},
+ {true, true, true},
+ },
+ 6: {
+ {true, true, true},
+ {true, false, false},
+ {true, true, true},
+ {true, false, true},
+ {true, true, true},
+ },
+ 7: {
+ {true, true, true},
+ {false, false, true},
+ {false, true, false},
+ {false, true, false},
+ {false, true, false},
+ },
+ 8: {
+ {true, true, true},
+ {true, false, true},
+ {true, true, true},
+ {true, false, true},
+ {true, true, true},
+ },
+ 9: {
+ {true, true, true},
+ {true, false, true},
+ {true, true, true},
+ {false, false, true},
+ {true, true, true},
+ },
+}
+
+var SymbolPatterns = map[string]Pattern{
+ "celsius": {
+ {true, false, false, false, false},
+ {false, false, true, true, false},
+ {false, true, false, false, false},
+ {false, true, false, false, false},
+ {false, false, true, true, false},
+ },
+ "fahrenheit": {
+ {true, false, true, true, false},
+ {false, false, true, false, false},
+ {false, false, true, true, false},
+ {false, false, true, false, false},
+ {false, false, true, false, false},
+ },
+ "percent": {
+ {false, false, false, false, false},
+ {false, true, false, false, true},
+ {false, false, false, true, false},
+ {false, false, true, false, false},
+ {false, true, false, false, true},
+ },
+}
+
+func InsertPattern(grid [][]bool, pattern Pattern, row, col int) {
+ for i, rowPattern := range pattern {
+ for j, val := range rowPattern {
+ if row+i < len(grid) && col+j < len(grid[0]) {
+ grid[row+i][col+j] = val
+ }
+ }
+ }
+}
+
+func CreateNumberGrid(value int, symbol string, row int) ([][]bool, error) {
+ if value < 0 || value >= 1000 {
+ return nil, errors.New("value must be between 0 and 999")
+ }
+ if _, ok := SymbolPatterns[symbol]; !ok {
+ return nil, errors.New("unsupported symbol")
+ }
+
+ grid := make([][]bool, 14)
+ for i := range grid {
+ grid[i] = make([]bool, 14)
+ }
+
+ var (
+ digits []int
+ symbolCol int
+ )
+
+ if value < 100 {
+ digits = []int{value / 10, value % 10}
+ symbolCol = 9
+ } else {
+ digits = []int{value / 100, (value % 100) / 10, value % 10}
+ symbolCol = 13
+ }
+
+ col := 1
+ for _, digit := range digits {
+ InsertPattern(grid, DigitPatterns[digit], row, col)
+ col += 4
+ }
+ InsertPattern(grid, SymbolPatterns[symbol], row, symbolCol)
+
+ return grid, nil
+}