-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtool_get_battery_info.go
More file actions
152 lines (135 loc) · 3.54 KB
/
Copy pathtool_get_battery_info.go
File metadata and controls
152 lines (135 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//go:build linux
package main
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
// BatteryInfoResult holds the get_battery_info response.
type BatteryInfoResult struct {
Level string `json:"level"`
Status string `json:"status"`
Temperature string `json:"temperature,omitempty"`
Voltage string `json:"voltage,omitempty"`
Technology string `json:"technology,omitempty"`
Health string `json:"health,omitempty"`
Error string `json:"error,omitempty"`
}
func registerGetBatteryInfo(s *server.MCPServer) {
tool := mcp.NewTool("get_battery_info",
mcp.WithDescription(
"Get battery information (level, status, temperature, voltage) "+
"from /sys/class/power_supply/ sysfs entries. More reliable "+
"than binder for shell UID.",
),
mcp.WithReadOnlyHintAnnotation(true),
mcp.WithDestructiveHintAnnotation(false),
mcp.WithIdempotentHintAnnotation(true),
)
s.AddTool(tool, handleGetBatteryInfo)
}
func handleGetBatteryInfo(
ctx context.Context,
_ mcp.CallToolRequest,
) (*mcp.CallToolResult, error) {
logger.Tracef(ctx, "handleGetBatteryInfo")
defer func() { logger.Tracef(ctx, "/handleGetBatteryInfo") }()
result := BatteryInfoResult{}
// Try dumpsys battery first (most reliable on Android).
out, err := shellExec("dumpsys battery")
if err == nil {
result = parseBatteryDumpsys(out)
} else {
// Fall back to sysfs.
result = readBatterySysfs()
}
data, err := json.Marshal(result)
if err != nil {
return nil, fmt.Errorf("marshaling battery info: %w", err)
}
return mcp.NewToolResultText(string(data)), nil
}
func parseBatteryDumpsys(output string) BatteryInfoResult {
result := BatteryInfoResult{}
for _, line := range strings.Split(output, "\n") {
line = strings.TrimSpace(line)
parts := strings.SplitN(line, ":", 2)
if len(parts) != 2 {
continue
}
key := strings.TrimSpace(parts[0])
val := strings.TrimSpace(parts[1])
switch key {
case "level":
result.Level = val
case "status":
result.Status = batteryStatusName(val)
case "temperature":
result.Temperature = val
case "voltage":
result.Voltage = val
case "technology":
result.Technology = val
case "health":
result.Health = batteryHealthName(val)
}
}
return result
}
func readBatterySysfs() BatteryInfoResult {
result := BatteryInfoResult{}
readSysfs := func(path string) string {
out, err := shellExec("cat " + path + " 2>/dev/null")
if err != nil {
return ""
}
return strings.TrimSpace(out)
}
result.Level = readSysfs("/sys/class/power_supply/battery/capacity")
result.Status = readSysfs("/sys/class/power_supply/battery/status")
result.Temperature = readSysfs("/sys/class/power_supply/battery/temp")
result.Voltage = readSysfs("/sys/class/power_supply/battery/voltage_now")
result.Technology = readSysfs("/sys/class/power_supply/battery/technology")
result.Health = readSysfs("/sys/class/power_supply/battery/health")
return result
}
func batteryStatusName(code string) string {
switch code {
case "1":
return "unknown"
case "2":
return "charging"
case "3":
return "discharging"
case "4":
return "not_charging"
case "5":
return "full"
default:
return code
}
}
func batteryHealthName(code string) string {
switch code {
case "1":
return "unknown"
case "2":
return "good"
case "3":
return "overheat"
case "4":
return "dead"
case "5":
return "over_voltage"
case "6":
return "failure"
case "7":
return "cold"
default:
return code
}
}