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