wwan.go (2169B)
1 package wwan 2 3 import ( 4 "bufio" 5 "bytes" 6 "context" 7 "fmt" 8 "net" 9 "os/exec" 10 "path" 11 "strings" 12 ) 13 14 type Block struct { 15 iface string 16 } 17 18 func New(iface string) *Block { 19 return &Block{iface: iface} 20 } 21 22 func (b *Block) Name() string { 23 return "wwan" 24 } 25 26 // TODO: signal strength icon. 27 // TODO: get IP address (net.Interfaces). 28 func (b *Block) Render(ctx context.Context) (string, error) { 29 out, err := exec.CommandContext(ctx, "mmcli", "--list-modems").Output() 30 if err != nil { 31 return "", fmt.Errorf("exec mmcli failed: %v", err) 32 } 33 34 fields := strings.Fields(string(out)) 35 if len(fields) == 0 { 36 return "", fmt.Errorf("unexpected output: %v", err) 37 } 38 modem := path.Base(fields[0]) 39 40 out, err = exec.CommandContext( 41 ctx, "mmcli", "-m", modem, "--output-keyvalue", 42 ).Output() 43 if err != nil { 44 return "", fmt.Errorf("exec mmcli failed: %v", err) 45 } 46 47 var ( 48 scanner = bufio.NewScanner(bytes.NewReader(out)) 49 50 state, signal string 51 ) 52 for scanner.Scan() { 53 fields := strings.Fields(scanner.Text()) 54 if len(fields) < 3 { 55 continue 56 } 57 58 switch { 59 case fields[0] == "modem.generic.state": 60 state = fields[2] 61 case fields[0] == "modem.generic.signal-quality.value": 62 signal = fields[2] 63 } 64 } 65 66 if state == "disabled" { 67 return " ", nil 68 } 69 70 // Get the IP address. 71 iface, err := net.InterfaceByName(b.iface) 72 if err != nil { 73 return "", fmt.Errorf( 74 "failed to get interface %s: %v", b.iface, err, 75 ) 76 } 77 78 var ip4, ip6 string 79 addrs, err := iface.Addrs() 80 if err != nil { 81 return "", fmt.Errorf("failed to get addresses: %v", err) 82 } 83 84 for _, addr := range addrs { 85 a := addr.String() 86 if isIPv4(a) { 87 ip4 = a 88 continue 89 } else { 90 ip6 = a 91 } 92 } 93 94 switch { 95 case ip4 == "" && ip6 == "": 96 return fmt.Sprintf(" %s%%", signal), nil 97 case ip4 != "" && ip6 == "": 98 return fmt.Sprintf(" %s %s%%", ip4, signal), nil 99 case ip4 == "" && ip6 != "": 100 return fmt.Sprintf(" %s %s%%", ip6, signal), nil 101 default: 102 return fmt.Sprintf(" %s %s %s%%", ip4, ip6, signal), nil 103 } 104 } 105 106 func isIPv4(s string) bool { 107 for i := 0; i < len(s); i++ { 108 switch s[i] { 109 case '.': 110 return true 111 case ':': 112 return false 113 } 114 } 115 116 return false 117 }