209 lines
No EOL
4.8 KiB
Go
209 lines
No EOL
4.8 KiB
Go
package providers
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"github.com/codemicro/bar/internal/i3bar"
|
|
)
|
|
|
|
const (
|
|
batteryStateFull = "FUL" // "full"
|
|
batteryStateDischarging = "BAT"
|
|
batteryStateCharging = "CHR"
|
|
batteryStateNotCharging = "HLD" // for "hold" - present when charge threshold set
|
|
batteryStateUnknown = "UNK"
|
|
)
|
|
|
|
type Battery struct {
|
|
FullThreshold float32
|
|
OkThreshold float32
|
|
WarningThreshold float32
|
|
|
|
DeviceName string
|
|
UseDesignMaxEnergy bool
|
|
|
|
name string
|
|
previousWasBackgroundWarning bool
|
|
isAlert bool
|
|
previousValue string
|
|
}
|
|
|
|
func NewBattery(deviceName string, fullThreshold, okThreshold, warningThreshold float32) i3bar.BlockGenerator {
|
|
return &Battery{
|
|
DeviceName: deviceName,
|
|
FullThreshold: fullThreshold,
|
|
OkThreshold: okThreshold,
|
|
WarningThreshold: warningThreshold,
|
|
name: "battery",
|
|
}
|
|
}
|
|
|
|
func (g *Battery) Frequency() uint8 {
|
|
if g.isAlert {
|
|
return 1
|
|
}
|
|
return 5
|
|
}
|
|
|
|
func (g *Battery) infoPath() string {
|
|
return path.Join("/sys/class/power_supply", g.DeviceName)
|
|
}
|
|
|
|
func (g *Battery) getPercentage() (float32, error) {
|
|
// TODO: Cache the read of energy_full
|
|
var maxEnergy, energyNow int32
|
|
{
|
|
maxFile := "energy_full"
|
|
if g.UseDesignMaxEnergy {
|
|
maxFile = "energy_full_design"
|
|
}
|
|
|
|
me, err := ioutil.ReadFile(path.Join(g.infoPath(), maxFile))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
en, err := ioutil.ReadFile(path.Join(g.infoPath(), "energy_now"))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
mei, err := strconv.ParseInt(strings.TrimSpace(string(me)), 10, 32)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
eni, err := strconv.ParseInt(strings.TrimSpace(string(en)), 10, 32)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
maxEnergy = int32(mei)
|
|
energyNow = int32(eni)
|
|
}
|
|
|
|
return (float32(energyNow) / float32(maxEnergy)) * 100, nil
|
|
}
|
|
|
|
func (g *Battery) getState() (string, error) {
|
|
|
|
sa, err := ioutil.ReadFile(path.Join(g.infoPath(), "status"))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var x string
|
|
|
|
switch strings.TrimSpace(string(sa)) {
|
|
case "Full":
|
|
x = batteryStateFull
|
|
case "Discharging":
|
|
x = batteryStateDischarging
|
|
case "Charging":
|
|
x = batteryStateCharging
|
|
case "Not charging":
|
|
x = batteryStateNotCharging
|
|
case "Unknown":
|
|
fallthrough
|
|
default:
|
|
x = batteryStateUnknown
|
|
}
|
|
|
|
return x, nil
|
|
}
|
|
|
|
func (g *Battery) Block(colors *i3bar.ColorSet) (*i3bar.Block, error) {
|
|
percentage, err := g.getPercentage()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
state, err := g.getState()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
percentageString := fmt.Sprintf("%.1f", percentage)
|
|
shortPercentageString := strings.Split(percentageString, ".")[0]
|
|
if g.previousValue != shortPercentageString && state == batteryStateDischarging {
|
|
switch shortPercentageString {
|
|
case "20":
|
|
_ = g.SendNotification("Battery is getting low", "At 20% and discharging. Consider plugging in.", "low")
|
|
case "12":
|
|
_ = g.SendNotification("Battery is getting lower", "At 12% and discharging. Plug in now.", "normal")
|
|
case "5":
|
|
_ = g.SendNotification("Battery is really low", "At 5% and discharging. PLUG IN NOW.", "critical")
|
|
}
|
|
}
|
|
g.previousValue = shortPercentageString
|
|
|
|
block := &i3bar.Block{
|
|
Name: g.name,
|
|
Instance: g.DeviceName,
|
|
FullText: fmt.Sprintf("%s %s%%", state, percentageString),
|
|
ShortText: percentageString,
|
|
}
|
|
|
|
if percentage < g.WarningThreshold && g.WarningThreshold != 0 {
|
|
|
|
g.isAlert = true
|
|
|
|
if g.previousWasBackgroundWarning || state == batteryStateCharging { // disable flashing when on charge
|
|
block.TextColor = colors.Bad
|
|
} else {
|
|
block.BackgroundColor = colors.Bad
|
|
}
|
|
|
|
g.previousWasBackgroundWarning = !g.previousWasBackgroundWarning
|
|
|
|
} else if percentage < g.OkThreshold && g.OkThreshold != 0 {
|
|
block.TextColor = colors.Warning
|
|
} else {
|
|
g.isAlert = false
|
|
}
|
|
|
|
switch state {
|
|
case batteryStateCharging:
|
|
if percentage > g.FullThreshold && g.FullThreshold != 0 {
|
|
block.TextColor = colors.Good
|
|
} else {
|
|
// Set text/background color to white
|
|
block.BackgroundColor = nil
|
|
block.TextColor = nil
|
|
}
|
|
case batteryStateFull:
|
|
block.BackgroundColor = colors.Warning
|
|
block.TextColor = colors.Background
|
|
case batteryStateUnknown:
|
|
block.TextColor = colors.Warning
|
|
}
|
|
|
|
return block, nil
|
|
}
|
|
|
|
func (g *Battery) GetNameAndInstance() (string, string) {
|
|
return g.name, g.DeviceName
|
|
}
|
|
|
|
func (g *Battery) SendNotification(summary, message, priority string) error {
|
|
fname, err := exec.LookPath("notify-send")
|
|
if err == nil {
|
|
fname, err = filepath.Abs(fname)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
process, err := os.StartProcess(fname, []string{fname, "--urgency", priority, summary, message}, &os.ProcAttr{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_ = process.Release()
|
|
return nil
|
|
} |