master
go 146 lines 3.02 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package apcupsd
4
5 import (
6 "context"
7 "errors"
8 "fmt"
9 "strings"
10 "time"
11 )
12
13 const precision = 100
14
15 func (c *Collector) collect() (map[string]int64, error) {
16 if c.conn == nil {
17 conn, err := c.establishConnection()
18 if err != nil {
19 return nil, err
20 }
21 c.conn = conn
22 }
23
24 resp, err := c.conn.status()
25 if err != nil {
26 c.Cleanup(context.Background())
27 return nil, err
28 }
29 c.Debugf("apcupsd response: %s", string(resp))
30
31 mx := make(map[string]int64)
32
33 if err := c.collectStatus(mx, resp); err != nil {
34 return nil, err
35 }
36
37 return mx, nil
38 }
39
40 func (c *Collector) collectStatus(mx map[string]int64, resp []byte) error {
41 st, err := parseStatus(resp)
42 if err != nil {
43 return fmt.Errorf("failed to parse status: %v", err)
44 }
45
46 if st.status == "" {
47 return errors.New("unexpected response: status is empty")
48 }
49
50 for _, v := range upsStatuses {
51 mx["status_"+v] = 0
52 }
53 for v := range strings.FieldsSeq(st.status) {
54 mx["status_"+v] = 1
55 }
56
57 switch st.status {
58 case "COMMLOST", "SHUTTING_DOWN":
59 return nil
60 }
61
62 if st.selftest != "" {
63 for _, v := range upsSelftestStatuses {
64 mx["selftest_"+v] = 0
65 }
66 mx["selftest_"+st.selftest] = 1
67 }
68
69 if st.bcharge != nil {
70 mx["battery_charge"] = int64(*st.bcharge * precision)
71 }
72 if st.battv != nil {
73 mx["battery_voltage"] = int64(*st.battv * precision)
74 }
75 if st.nombattv != nil {
76 mx["battery_voltage_nominal"] = int64(*st.nombattv * precision)
77 }
78 if st.linev != nil {
79 mx["input_voltage"] = int64(*st.linev * precision)
80 }
81 if st.minlinev != nil {
82 mx["input_voltage_min"] = int64(*st.minlinev * precision)
83 }
84 if st.maxlinev != nil {
85 mx["input_voltage_max"] = int64(*st.maxlinev * precision)
86 }
87 if st.linefreq != nil {
88 mx["input_frequency"] = int64(*st.linefreq * precision)
89 }
90 if st.outputv != nil {
91 mx["output_voltage"] = int64(*st.outputv * precision)
92 }
93 if st.nomoutv != nil {
94 mx["output_voltage_nominal"] = int64(*st.nomoutv * precision)
95 }
96 if st.loadpct != nil {
97 mx["load_percent"] = int64(*st.loadpct * precision)
98 }
99 if st.itemp != nil {
100 mx["itemp"] = int64(*st.itemp * precision)
101 }
102 if st.timeleft != nil {
103 mx["timeleft"] = int64(*st.timeleft * 60 * precision) // to seconds
104 }
105 if st.nompower != nil && st.loadpct != nil {
106 mx["load"] = int64(*st.nompower * *st.loadpct)
107 }
108 if st.battdate != "" {
109 if v, err := battdateSecondsAgo(st.battdate); err != nil {
110 c.Debugf("failed to calculate time since battery replacement for date '%s': %v", st.battdate, err)
111 } else {
112 mx["battery_seconds_since_replacement"] = v
113 }
114 }
115
116 return nil
117 }
118
119 func battdateSecondsAgo(battdate string) (int64, error) {
120 var layout string
121
122 if strings.ContainsRune(battdate, '-') {
123 layout = "2006-01-02"
124 } else {
125 layout = "01/02/06"
126 }
127
128 date, err := time.Parse(layout, battdate)
129 if err != nil {
130 return 0, err
131 }
132
133 secsAgo := int64(time.Since(date).Seconds())
134
135 return secsAgo, nil
136 }
137
138 func (c *Collector) establishConnection() (apcupsdConn, error) {
139 conn := c.newConn(c.Config)
140
141 if err := conn.connect(); err != nil {
142 return nil, err
143 }
144
145 return conn, nil
146 }