1 Commits

Author SHA1 Message Date
eleksir
64da06663c Add sysfs method to get battery charge for linux 2025-04-24 19:49:50 +03:00
11 changed files with 33 additions and 390 deletions

View File

@@ -4,7 +4,6 @@ BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath -buildvcs=false
BINARY=i3status-go
TEST1=battery-test
TEST2=cmdrun-test
## Use calssic targets where first one is deafult target
all: clean build
@@ -17,18 +16,15 @@ battery-test:
rm -rf ${TEST1}
go build ${BUILDOPTS} -o ${TEST1} ./cmd/${TEST1}
cmdrun-test:
rm -rf ${TEST1}
go build ${BUILDOPTS} -o ${TEST2} ./cmd/${TEST2}
## Remove binary with golang compiler' means
clean:
rm -rf ${BINARY} ${TEST1} ${TEST2}
rm -rf ${BINARY} ${TEST1}
## Misc target, for development purposes. Updates vendored libs, brutal way.
upgrade:
$(RM) -r vendor
go get -u -t ./...
go get -u -t -tool ./...
go mod tidy
go mod vendor

View File

@@ -14,12 +14,11 @@ It is i3bar text generator.
* PulseAudio volume indicator, can adjust master volume too
* Clock
* Cron jobs (intended to use for show periodic desktop notifications but not limited to)
* Show output of one-shot system command
## How to build it
This application was tested to compile with go v1.24.
This appliction was tested to compile with go v1.21 but can be built with earlier versions (seems down to 1.18, not
sure).
All you need is go lang compiler and gnu make utility. Invoke
```bash

View File

@@ -702,78 +702,6 @@
]
},
// Prints stdout of command to panel.
"cmdrun" : {
"enabled": false,
// Delay between two consecutive command batch runs.
"delay": 3,
// command' binary.
"cmd": "/bin/bash",
// Arguments for command binary.
"args": ["-c", "echo 42"],
// If omitted set to default color defined up here.
"color": "#3e78fd",
// If omitted set to default background color defined up here.
"background": "#000000",
// If omitted set to default font defined up here.
"font": "Noto Sans",
// ‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’, ‘x-large’, ‘xx-large’, or one of relative sizes
// ‘smaller’ or ‘larger’. If omitted set to default font size defined up here.
"font_size": "medium",
// Re-define separator parameters for each command block here.
"separator": {
"left": {
// Set to false if omitted.
"enabled": true,
// If omitted set to default color defined for separator up here.
"color": "#3e78fd",
// If omitted set to default background color defined for separator up here.
"background": "#000000",
// If omitted set to default symbol defined for separator up here.
"symbol": "▶",
// If omitted set to default font defined for separator up here.
"font": "Noto Sans",
// ‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’, ‘x-large’, ‘xx-large’, or one of relative sizes
// ‘smaller’ or ‘larger’. If omitted set to default font size defined for separator up here.
"font_size": "medium"
},
"right": {
// Set to false if omitted.
"enabled": true,
// If omitted set to default color defined for separator up here.
"color": "#3e78fd",
// If omitted set to default background color defined for separator up here.
"background": "#000000",
// If omitted set to default symbol defined for separator up here.
"symbol": "◀",
// If omitted set to default font defined for separator up here.
"font": "Noto Sans",
// ‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’, ‘x-large’, ‘xx-large’, or one of relative sizes
// ‘smaller’ or ‘larger’. If omitted set to default font size defined for separator up here.
"font_size": "medium"
}
}
}
// Whether to display Application Buttons.
"app_buttons": {
"enabled": true,

View File

@@ -84,14 +84,6 @@ func main() {
Conf.Clock.FontSize,
)
Conf.Values.RunCommandOutput = fmt.Sprintf(
"<span color='%s' background='%s' font='%s' size='%s'>?</span>",
Conf.CmdRun.Color,
Conf.CmdRun.Background,
Conf.CmdRun.Font,
Conf.CmdRun.FontSize,
)
go Conf.Spawner()
go Conf.ParseStdin()
go Conf.CleanZombies()
@@ -151,10 +143,6 @@ func main() {
go Conf.UpdateVPNStatus()
}
if Conf.CmdRun.Enabled {
go Conf.RunCommand()
}
/*
I3bar documentation pretends that message protocol must be valid json. In practice, we only have to print valid
header, empty json array and (potentially infinite) json lines (line that is valid json by itself) that is
@@ -531,50 +519,6 @@ func main() {
j = append(j, b)
}
if Conf.CmdRun.Enabled {
var b lib.I3BarOutBlock
b.Name = `runcommandoutput`
b.Color = Conf.CmdRun.Color
b.Background = Conf.CmdRun.Background
if Conf.CmdRun.Separator.Left.Enabled {
b.FullText = fmt.Sprintf(
"<span color='%s' background='%s' font='%s' size='%s'>%s</span>",
Conf.CmdRun.Separator.Left.Color,
Conf.CmdRun.Separator.Left.Background,
Conf.CmdRun.Separator.Left.Font,
Conf.CmdRun.Separator.Left.FontSize,
Conf.CmdRun.Separator.Left.Symbol,
)
}
b.FullText += fmt.Sprintf(
"<span color='%s' background='%s' font='%s' size='%s'>%s</span>",
Conf.CmdRun.Color,
Conf.CmdRun.Background,
Conf.CmdRun.Font,
Conf.CmdRun.FontSize,
Conf.Values.RunCommandOutput,
)
if Conf.CmdRun.Separator.Right.Enabled {
b.FullText += fmt.Sprintf(
"<span color='%s' background='%s' font='%s' size='%s'>%s</span>",
Conf.CmdRun.Separator.Right.Color,
Conf.CmdRun.Separator.Right.Background,
Conf.CmdRun.Separator.Right.Font,
Conf.CmdRun.Separator.Right.FontSize,
Conf.CmdRun.Separator.Right.Symbol,
)
}
b.Markup = "pango"
b.Separator = false
j = append(j, b)
}
if Conf.Clock.Enabled {
var b lib.I3BarOutBlock

View File

@@ -1 +1 @@
../cmd/i3status-go/default-config.json
/home/eleksir/git/i3status-go/cmd/i3status-go/default-config.json

View File

@@ -16,10 +16,14 @@ import (
// UpdateBatteryInfo updates info about battery charge.
func (c *MyConfig) UpdateBatteryInfo() {
var (
ch int
status string
Batts string
InitialDelay = 100 * time.Millisecond
LoopIterationDelay = 5 * time.Second
Delay = InitialDelay
ticker = time.NewTicker(Delay)
batteries []*battery.Battery
)
for range ticker.C {
@@ -28,17 +32,8 @@ func (c *MyConfig) UpdateBatteryInfo() {
ticker.Reset(Delay)
}
var (
batteries = []*battery.Battery{}
Batts string
ch int
status string
)
if c.Battery.UseSysfs {
for _, file := range c.Battery.SysfsFiles {
var batt battery.Battery
for battNum, file := range c.Battery.SysfsFiles {
_, err := os.Stat(file)
// If file exists.
@@ -52,13 +47,12 @@ func (c *MyConfig) UpdateBatteryInfo() {
if err != nil {
log.Printf("Unable to read file %s: %s", file, err)
continue
}
myCharge = strings.Trim(string(b), "\n")
statusFile := filepath.Dir(file) + "/status"
statusFile := filepath.Dir(file) + "status"
// If file exists.
if !os.IsNotExist(err) {
@@ -66,7 +60,6 @@ func (c *MyConfig) UpdateBatteryInfo() {
if err != nil {
log.Printf("Unable to read file %s: %s", statusFile, err)
continue
}
@@ -78,34 +71,31 @@ func (c *MyConfig) UpdateBatteryInfo() {
// Make stub battery class :) .
switch myStatus {
case "Charging":
batt.State.Raw = battery.Charging
batteries[battNum].State.Raw = battery.Charging
case "Discharging":
batt.State.Raw = battery.Discharging
batteries[battNum].State.Raw = battery.Discharging
case "Empty":
batt.State.Raw = battery.Empty
batteries[battNum].State.Raw = battery.Empty
case "Full":
batt.State.Raw = battery.Full
batteries[battNum].State.Raw = battery.Full
default:
batt.State.Raw = battery.Unknown
batteries[battNum].State.Raw = battery.Unknown
}
ch, err := strconv.Atoi(myCharge)
if err != nil {
log.Printf("Unable to convert string from file %s to integer", file)
continue
}
batt.Current = float64(ch)
batteries = append(batteries, &batt)
batteries[battNum].Current = float64(ch)
}
}
} else {
batteries, _ = battery.GetAll()
// In theory, this module should give for each entry its separate err, but in practice it gives
// one single err for all entries, so we cannot detemine whist exatly entry errored.
batteries, _ = battery.GetAll()
}
var (

View File

@@ -59,17 +59,16 @@ type Separator struct {
type MyConfig struct {
Values struct {
PrintOutput bool
CPUTemperature int64
BatteryString string
ClockTime string
IfStatus string
VPNStatus string
Memory Mem
La string
PA *p.Client
SoundVolume string
RunCommandOutput string
PrintOutput bool
CPUTemperature int64
BatteryString string
ClockTime string
IfStatus string
VPNStatus string
Memory Mem
La string
PA *p.Client
SoundVolume string
}
Channels struct {
@@ -232,18 +231,6 @@ type MyConfig struct {
} `json:"tasks,omitempty"`
} `json:"cron,omitempty"`
CmdRun struct {
Enabled bool `json:"enabled,omitempty"`
Color string `json:"color,omitempty"`
Background string `json:"background,omitempty"`
Font string `json:"font,omitempty"`
FontSize string `json:"font_size,omitempty"`
Separator Separator `json:"separator,omitempty"`
Cmd string `json:"cmd,omitempty"`
Delay int `json:"delay,omitempty"`
Args []string `json:"args,omitempty"`
}
AppButtons struct {
Enabled bool `json:"enabled,omitempty"`
Separator Separator `json:"separator,omitempty"`
@@ -796,10 +783,7 @@ func ReadConf(defaultConfig []byte) (*MyConfig, error) {
// sampleConfig.Battery.UseSysfs will be false if not set in config
if len(sampleConfig.Battery.SysfsFiles) == 0 {
sampleConfig.Battery.SysfsFiles = append(
sampleConfig.Battery.SysfsFiles,
"/sys/class/power_supply/BAT0/capacity",
)
sampleConfig.Battery.SysfsFiles[0] = "/sys/class/power_supply/BAT0/capacity"
}
if sampleConfig.Battery.Color == "" {
@@ -1049,7 +1033,7 @@ func ReadConf(defaultConfig []byte) (*MyConfig, error) {
}
if sampleConfig.Vpn.Font == "" {
sampleConfig.Vpn.Font = sampleConfig.Font
sampleConfig.Vpn.FontSize = sampleConfig.Font
}
if sampleConfig.Vpn.FontSize == "" {
@@ -1459,122 +1443,6 @@ func ReadConf(defaultConfig []byte) (*MyConfig, error) {
}
}
// sampleConfig.CmdRun.Enabled will false if not set in config
if sampleConfig.CmdRun.Cmd == "" && sampleConfig.CmdRun.Enabled {
sampleConfig.CmdRun.Enabled = false
log.Print(
"Setting sampleConfig.CmdRun.Enabled to false because sampleConfig.CmdRun.Cmd is not set",
)
}
// sampleConfig.CmdRun.Args can be empty
if sampleConfig.CmdRun.Color == "" {
sampleConfig.CmdRun.Color = sampleConfig.Color
}
if sampleConfig.CmdRun.Background == "" {
sampleConfig.CmdRun.Background = sampleConfig.Background
}
if sampleConfig.CmdRun.Font == "" {
sampleConfig.CmdRun.Font = sampleConfig.Font
}
if sampleConfig.CmdRun.FontSize == "" {
sampleConfig.CmdRun.FontSize = sampleConfig.FontSize
}
if sampleConfig.CmdRun.Separator.Left.Color == "" {
sampleConfig.CmdRun.Separator.Left.Color = sampleConfig.Separator.Left.Color
}
if sampleConfig.CmdRun.Separator.Left.Background == "" {
sampleConfig.CmdRun.Separator.Left.Background = sampleConfig.Separator.Left.Background
}
if sampleConfig.CmdRun.Separator.Left.Symbol == "" {
sampleConfig.CmdRun.Separator.Left.Symbol = sampleConfig.Separator.Left.Symbol
}
if sampleConfig.CmdRun.Separator.Left.Font == "" {
sampleConfig.CmdRun.Separator.Left.Font = sampleConfig.Separator.Left.Font
}
if sampleConfig.CmdRun.Separator.Left.FontSize == "" {
sampleConfig.CmdRun.Separator.Left.FontSize = sampleConfig.Separator.Left.FontSize
} else {
matched, err := regexp.MatchString(
`^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger)$`,
sampleConfig.CmdRun.Separator.Left.FontSize,
)
if err != nil {
log.Printf(
"Unable to set sampleConfig.CmdRun.Separator.Left.FontSize: %s, fallback to %s",
err,
sampleConfig.Separator.Left.FontSize,
)
sampleConfig.CmdRun.Separator.Left.FontSize = sampleConfig.Separator.Left.FontSize
}
if !matched {
log.Printf(
"Unable to set sampleConfig.CmdRun.Separator.Left.FontSize, fallback to %s",
sampleConfig.Separator.Left.FontSize,
)
sampleConfig.CmdRun.Separator.Left.FontSize = sampleConfig.Separator.Left.FontSize
}
}
if sampleConfig.CmdRun.Separator.Right.Color == "" {
sampleConfig.CmdRun.Separator.Right.Color = sampleConfig.Separator.Right.Color
}
if sampleConfig.CmdRun.Separator.Right.Background == "" {
sampleConfig.CmdRun.Separator.Right.Background = sampleConfig.Separator.Right.Background
}
if sampleConfig.CmdRun.Separator.Right.Symbol == "" {
sampleConfig.CmdRun.Separator.Right.Symbol = sampleConfig.Separator.Right.Symbol
}
if sampleConfig.CmdRun.Separator.Right.Font == "" {
sampleConfig.CmdRun.Separator.Right.Font = sampleConfig.Separator.Right.Font
}
if sampleConfig.CmdRun.Separator.Right.FontSize == "" {
sampleConfig.CmdRun.Separator.Right.FontSize = sampleConfig.Separator.Right.FontSize
} else {
matched, err := regexp.MatchString(
`^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger)$`,
sampleConfig.CmdRun.Separator.Right.FontSize,
)
if err != nil {
log.Printf(
"Unable to set sampleConfig.CmdRun.Separator.Right.FontSize: %s, fallback to %s",
err,
sampleConfig.Separator.Right.FontSize,
)
sampleConfig.CmdRun.Separator.Right.FontSize = sampleConfig.Separator.Right.FontSize
}
if !matched {
log.Printf(
"Unable to set sampleConfig.CmdRun.Separator.Right.FontSize, fallback to %s",
sampleConfig.Separator.Right.FontSize,
)
sampleConfig.CmdRun.Separator.Right.FontSize = sampleConfig.Separator.Right.FontSize
}
}
// sampleConfig.AppButtons.Enabled will false if not set in config
if sampleConfig.AppButtons.Separator.Left.Color == "" {

View File

@@ -51,7 +51,7 @@ func (c *MyConfig) UpdateMemStats() {
if c.Values.Memory.Usedpct != uint64(v.UsedPercent) || c.Values.Memory.Shared != v.Shared/1024/1024 || c.Values.Memory.Swap != v.SwapTotal-v.SwapFree {
c.Values.Memory.Usedpct = uint64(v.UsedPercent)
c.Values.Memory.Shared = v.Shared / 1024 / 1024
c.Values.Memory.Swap = sw.Used / 1024 / 1024
c.Values.Memory.Swap = sw.Used
c.Channels.UpdateReady <- true
}
} else {

View File

@@ -1,41 +0,0 @@
package lib
import (
"log"
"time"
)
// RunCommands gets list of commands runs them sequentally and conacatenates their output.
func (c *MyConfig) RunCommand() {
var (
InitialDelay = 100 * time.Millisecond
LoopIterationDelay = time.Duration(c.CmdRun.Delay) * time.Second
Delay = InitialDelay
ticker = time.NewTicker(Delay)
)
for range ticker.C {
if Delay == InitialDelay {
Delay = LoopIterationDelay
ticker.Reset(Delay)
}
var (
outputString string
command = []string{c.CmdRun.Cmd}
)
if len(c.CmdRun.Args) > 0 {
command = append(command, c.CmdRun.Args...)
}
outputString += RunProcess(command)
log.Println(c.Values.RunCommandOutput)
if c.Values.RunCommandOutput != outputString {
c.Values.RunCommandOutput = outputString
c.Channels.UpdateReady <- true
}
}
}

View File

@@ -1,11 +1,9 @@
package lib
import (
"bytes"
"log"
"os"
"os/exec"
"strings"
"syscall"
"time"
)
@@ -14,7 +12,6 @@ import (
func (c *MyConfig) Spawner() {
for prg := range c.Channels.RunChan {
<-time.After(25 * time.Millisecond)
devnullR, _ := os.Open(os.DevNull)
devnullW, _ := os.OpenFile(os.DevNull, os.O_WRONLY|os.O_APPEND, 0644)
@@ -58,41 +55,3 @@ func (c *MyConfig) CleanZombies() {
time.Sleep(1 * time.Minute)
}
}
// RunProcess runs given command and returns contens of command stdout. On error prints error on stderr and returns
// empty syting.
func RunProcess(command []string) string {
var (
cmd *exec.Cmd
stdout, stderr bytes.Buffer
)
switch len(command) {
case 1:
cmd = exec.Command(command[0], "") //nolint: gosec
default:
cmd = exec.Command(command[0], command[1:]...) //nolint: gosec
}
cmd.Dir = "/"
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
if stdout.String() != "" {
log.Printf("Unable to run '%s': %s, %s", strings.Join(command, " "), err, stdout.String())
} else {
log.Printf("Unable to run '%s': %s", strings.Join(command, " "), err)
}
return ""
}
if stderr.String() != "" {
log.Printf("%s: %s", strings.Join(command, " "), stderr.String())
}
return strings.TrimRight(stdout.String(), "\n")
}

View File

@@ -70,7 +70,7 @@ func (c *MyConfig) UpdateVPNStatus() {
func (c *MyConfig) VPNTCPCheck() bool {
conn, err := net.DialTimeout(
"tcp",
fmt.Sprintf("%s:%d", c.Vpn.TCPCheck.Host, c.Vpn.TCPCheck.Port), //nolint: hostport
fmt.Sprintf("%s:%d", c.Vpn.TCPCheck.Host, c.Vpn.TCPCheck.Port),
time.Second*time.Duration(c.Vpn.TCPCheck.Timeout),
)