diff options
author | Blaster4385 <venkatesh@tablaster.dev> | 2025-01-13 14:29:08 +0530 |
---|---|---|
committer | Blaster4385 <venkatesh@tablaster.dev> | 2025-01-13 16:48:16 +0530 |
commit | c068b6192ae1996a3c09fd73dfdee8adb36b43cc (patch) | |
tree | 6f311f6aba7039db8f80c2697def7ac377a9a9db /modules | |
parent | c9b29354af7819c294862b8942c28169a89778a8 (diff) |
feat: added support for displaying cpu temperature and usage
Diffstat (limited to 'modules')
-rw-r--r-- | modules/cpu.go | 159 | ||||
-rw-r--r-- | modules/numbers.go | 150 |
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 +} |