@@ -16,33 +16,43 @@ var (
16
16
)
17
17
18
18
type fritzCollector struct {
19
- InfoDesc * prometheus.Desc
20
- PresentDesc * prometheus.Desc
21
- TemperatureDesc * prometheus.Desc
22
- TemperatureOffsetDesc * prometheus.Desc
23
- EnergyWhDesc * prometheus.Desc
24
- PowerWDesc * prometheus.Desc
25
- SwitchState * prometheus.Desc
26
- SwitchMode * prometheus.Desc
27
- SwitchBoxLock * prometheus.Desc
28
- SwitchDeviceLock * prometheus.Desc
29
- ThermostatBatteryLow * prometheus.Desc
30
- ThermostatErrorCode * prometheus.Desc
19
+ Info * prometheus.Desc
20
+ Present * prometheus.Desc
21
+ Temperature * prometheus.Desc
22
+ TemperatureOffset * prometheus.Desc
23
+ EnergyWh * prometheus.Desc
24
+ PowerW * prometheus.Desc
25
+ SwitchState * prometheus.Desc
26
+ SwitchMode * prometheus.Desc
27
+ SwitchBoxLock * prometheus.Desc
28
+ SwitchDeviceLock * prometheus.Desc
29
+ ThermostatBatteryChargeLevel * prometheus.Desc
30
+ ThermostatBatteryLow * prometheus.Desc
31
+ ThermostatErrorCode * prometheus.Desc
32
+ ThermostatTempComfort * prometheus.Desc
33
+ ThermostatTempGoal * prometheus.Desc
34
+ ThermostatTempSaving * prometheus.Desc
35
+ ThermostatWindowOpen * prometheus.Desc
31
36
}
32
37
33
38
func (fc * fritzCollector ) Describe (ch chan <- * prometheus.Desc ) {
34
- ch <- fc .InfoDesc
35
- ch <- fc .PresentDesc
36
- ch <- fc .TemperatureDesc
37
- ch <- fc .TemperatureOffsetDesc
38
- ch <- fc .EnergyWhDesc
39
- ch <- fc .PowerWDesc
39
+ ch <- fc .Info
40
+ ch <- fc .Present
41
+ ch <- fc .Temperature
42
+ ch <- fc .TemperatureOffset
43
+ ch <- fc .EnergyWh
44
+ ch <- fc .PowerW
40
45
ch <- fc .SwitchState
41
46
ch <- fc .SwitchMode
42
47
ch <- fc .SwitchBoxLock
43
48
ch <- fc .SwitchDeviceLock
49
+ ch <- fc .ThermostatBatteryChargeLevel
44
50
ch <- fc .ThermostatBatteryLow
45
51
ch <- fc .ThermostatErrorCode
52
+ ch <- fc .ThermostatTempComfort
53
+ ch <- fc .ThermostatTempGoal
54
+ ch <- fc .ThermostatTempSaving
55
+ ch <- fc .ThermostatWindowOpen
46
56
}
47
57
48
58
func (fc * fritzCollector ) Collect (ch chan <- prometheus.Metric ) {
@@ -51,24 +61,29 @@ func (fc *fritzCollector) Collect(ch chan<- prometheus.Metric) {
51
61
52
62
if err != nil {
53
63
log .Println ("Unable to collect data:" , err )
54
- ch <- prometheus .NewInvalidMetric (fc .InfoDesc , err )
55
- ch <- prometheus .NewInvalidMetric (fc .PresentDesc , err )
56
- ch <- prometheus .NewInvalidMetric (fc .TemperatureDesc , err )
57
- ch <- prometheus .NewInvalidMetric (fc .TemperatureOffsetDesc , err )
58
- ch <- prometheus .NewInvalidMetric (fc .EnergyWhDesc , err )
59
- ch <- prometheus .NewInvalidMetric (fc .PowerWDesc , err )
64
+ ch <- prometheus .NewInvalidMetric (fc .Info , err )
65
+ ch <- prometheus .NewInvalidMetric (fc .Present , err )
66
+ ch <- prometheus .NewInvalidMetric (fc .Temperature , err )
67
+ ch <- prometheus .NewInvalidMetric (fc .TemperatureOffset , err )
68
+ ch <- prometheus .NewInvalidMetric (fc .EnergyWh , err )
69
+ ch <- prometheus .NewInvalidMetric (fc .PowerW , err )
60
70
ch <- prometheus .NewInvalidMetric (fc .SwitchState , err )
61
71
ch <- prometheus .NewInvalidMetric (fc .SwitchMode , err )
62
72
ch <- prometheus .NewInvalidMetric (fc .SwitchBoxLock , err )
63
73
ch <- prometheus .NewInvalidMetric (fc .SwitchDeviceLock , err )
74
+ ch <- prometheus .NewInvalidMetric (fc .ThermostatBatteryChargeLevel , err )
64
75
ch <- prometheus .NewInvalidMetric (fc .ThermostatBatteryLow , err )
65
76
ch <- prometheus .NewInvalidMetric (fc .ThermostatErrorCode , err )
77
+ ch <- prometheus .NewInvalidMetric (fc .ThermostatTempComfort , err )
78
+ ch <- prometheus .NewInvalidMetric (fc .ThermostatTempGoal , err )
79
+ ch <- prometheus .NewInvalidMetric (fc .ThermostatTempSaving , err )
80
+ ch <- prometheus .NewInvalidMetric (fc .ThermostatWindowOpen , err )
66
81
return
67
82
}
68
83
69
84
for _ , dev := range l .Devices {
70
85
ch <- prometheus .MustNewConstMetric (
71
- fc .InfoDesc ,
86
+ fc .Info ,
72
87
prometheus .GaugeValue ,
73
88
1.0 ,
74
89
dev .Identifier ,
@@ -81,7 +96,7 @@ func (fc *fritzCollector) Collect(ch chan<- prometheus.Metric) {
81
96
)
82
97
83
98
ch <- prometheus .MustNewConstMetric (
84
- fc .PresentDesc ,
99
+ fc .Present ,
85
100
prometheus .GaugeValue ,
86
101
float64 (dev .Present ),
87
102
dev .Identifier ,
@@ -90,59 +105,58 @@ func (fc *fritzCollector) Collect(ch chan<- prometheus.Metric) {
90
105
)
91
106
92
107
if dev .Present == 1 && dev .CanMeasureTemp () {
93
- if err := stringToFloatMetric (ch , fc .TemperatureDesc , dev .Temperature .FmtCelsius (), & dev ); err != nil {
108
+ if err := mustStringToFloatMetric (ch , fc .Temperature , dev .Temperature .FmtCelsius (), & dev ); err != nil {
94
109
log .Printf ("Unable to parse temperature data of \" %s\" : %v\n " , dev .Name , err )
95
110
}
96
111
97
- if err := stringToFloatMetric (ch , fc .TemperatureOffsetDesc , dev .Temperature .FmtOffset (), & dev ); err != nil {
112
+ if err := mustStringToFloatMetric (ch , fc .TemperatureOffset , dev .Temperature .FmtOffset (), & dev ); err != nil {
98
113
log .Printf ("Unable to parse temperature offset data of \" %s\" : %v\n " , dev .Name , err )
99
114
}
100
115
}
101
116
102
117
if dev .Present == 1 && dev .CanMeasurePower () {
103
- if err := stringToFloatMetric (ch , fc .EnergyWhDesc , dev .Powermeter .FmtEnergyWh (), & dev ); err != nil {
118
+ if err := mustStringToFloatMetric (ch , fc .EnergyWh , dev .Powermeter .FmtEnergyWh (), & dev ); err != nil {
104
119
log .Printf ("Unable to parse energy data of \" %s\" : %v\n " , dev .Name , err )
105
120
}
106
121
107
- if err := stringToFloatMetric (ch , fc .PowerWDesc , dev .Powermeter .FmtPowerW (), & dev ); err != nil {
122
+ if err := mustStringToFloatMetric (ch , fc .PowerW , dev .Powermeter .FmtPowerW (), & dev ); err != nil {
108
123
log .Printf ("Unable to parse power data of \" %s\" : %v\n " , dev .Name , err )
109
124
}
110
125
}
111
126
112
127
if dev .IsThermostat () {
113
- if batteryLow , err := strconv .ParseFloat (dev .Thermostat .BatteryLow , 64 ); err != nil {
114
- ch <- prometheus .NewInvalidMetric (fc .ThermostatBatteryLow , err )
128
+ // Battery charge level is optional
129
+ if err := canStringToFloatMetric (ch , fc .ThermostatBatteryChargeLevel , dev .Thermostat .BatteryChargeLevel , & dev ); err != nil {
130
+ log .Printf ("Unable to parse battery charge level of \" %s\" : %v\n " , dev .Name , err )
131
+ }
132
+
133
+ if err := mustStringToFloatMetric (ch , fc .ThermostatBatteryLow , dev .Thermostat .BatteryLow , & dev ); err != nil {
115
134
log .Printf ("Unable to parse battery low state of \" %s\" : %v\n " , dev .Name , err )
116
- } else {
117
- ch <- prometheus .MustNewConstMetric (
118
- fc .ThermostatBatteryLow ,
119
- prometheus .GaugeValue ,
120
- batteryLow ,
121
- dev .Identifier ,
122
- dev .Productname ,
123
- dev .Name ,
124
- )
125
135
}
126
136
127
- var errCode float64
128
- // Reset err so it can be used later to decide if we need to send the ThermostatErrCode metric
129
- err = nil
130
- if dev .Thermostat .ErrorCode != "" {
131
- errCode , err = strconv .ParseFloat (dev .Thermostat .ErrorCode , 64 )
132
- if err != nil {
133
- ch <- prometheus .NewInvalidMetric (fc .ThermostatErrorCode , err )
134
- log .Printf ("Unable to parse thermostat error code of \" %s\" : %v\n " , dev .Name , err )
135
- }
137
+ // Handle no error like error code 0
138
+ errCodeStr := dev .Thermostat .ErrorCode
139
+ if errCodeStr == "" {
140
+ errCodeStr = "0"
141
+ }
142
+ if err := mustStringToFloatMetric (ch , fc .ThermostatErrorCode , errCodeStr , & dev ); err != nil {
143
+ log .Printf ("Unable to parse thermostat error code of \" %s\" : %v\n " , dev .Name , err )
144
+ }
145
+
146
+ // Comfort, Goal and Saving temperature are optional
147
+ if err := canStringToFloatMetric (ch , fc .ThermostatTempComfort , dev .Thermostat .FmtComfortTemperature (), & dev ); err != nil {
148
+ log .Printf ("Unable to parse comfort temperature of \" %s\" : %v\n " , dev .Name , err )
149
+ }
150
+ if err := canStringToFloatMetric (ch , fc .ThermostatTempGoal , dev .Thermostat .FmtGoalTemperature (), & dev ); err != nil {
151
+ log .Printf ("Unable to parse goal temperature of \" %s\" : %v\n " , dev .Name , err )
152
+ }
153
+ if err := canStringToFloatMetric (ch , fc .ThermostatTempSaving , dev .Thermostat .FmtSavingTemperature (), & dev ); err != nil {
154
+ log .Printf ("Unable to parse saving temperature of \" %s\" : %v\n " , dev .Name , err )
136
155
}
137
- if err == nil {
138
- ch <- prometheus .MustNewConstMetric (
139
- fc .ThermostatErrorCode ,
140
- prometheus .GaugeValue ,
141
- errCode ,
142
- dev .Identifier ,
143
- dev .Productname ,
144
- dev .Name ,
145
- )
156
+
157
+ // Window Open is optional
158
+ if err := canStringToFloatMetric (ch , fc .ThermostatWindowOpen , dev .Thermostat .WindowOpen , & dev ); err != nil {
159
+ log .Printf ("Unable to parse window open state of \" %s\" : %v\n " , dev .Name , err )
146
160
}
147
161
}
148
162
@@ -165,39 +179,39 @@ func (fc *fritzCollector) Collect(ch chan<- prometheus.Metric) {
165
179
166
180
func NewFritzCollector () * fritzCollector {
167
181
return & fritzCollector {
168
- InfoDesc : prometheus .NewDesc (
182
+ Info : prometheus .NewDesc (
169
183
"fritzbox_device_info" ,
170
184
"Device information" ,
171
185
append (genericLabels ,
172
186
"internal_id" , "fw_version" , "manufacturer" , "functionbitmask" ,
173
187
),
174
188
prometheus.Labels {},
175
189
),
176
- PresentDesc : prometheus .NewDesc (
190
+ Present : prometheus .NewDesc (
177
191
"fritzbox_device_present" ,
178
192
"Device connected (1) or not (0)" ,
179
193
genericLabels ,
180
194
prometheus.Labels {},
181
195
),
182
- TemperatureDesc : prometheus .NewDesc (
196
+ Temperature : prometheus .NewDesc (
183
197
"fritzbox_temperature" ,
184
198
"Temperature measured at the device sensor in units of 0.1 °C" ,
185
199
genericLabels ,
186
200
prometheus.Labels {},
187
201
),
188
- TemperatureOffsetDesc : prometheus .NewDesc (
202
+ TemperatureOffset : prometheus .NewDesc (
189
203
"fritzbox_temperature_offset" ,
190
204
"Temperature offset (set by the user) in units of 0.1 °C" ,
191
205
genericLabels ,
192
206
prometheus.Labels {},
193
207
),
194
- EnergyWhDesc : prometheus .NewDesc (
208
+ EnergyWh : prometheus .NewDesc (
195
209
"fritzbox_energy" ,
196
210
"Absolute energy consumption (in Wh) since the device started operating" ,
197
211
genericLabels ,
198
212
prometheus.Labels {},
199
213
),
200
- PowerWDesc : prometheus .NewDesc (
214
+ PowerW : prometheus .NewDesc (
201
215
"fritzbox_power" ,
202
216
"Current power (in W), refreshed approx every 2 minutes" ,
203
217
genericLabels ,
@@ -227,6 +241,12 @@ func NewFritzCollector() *fritzCollector {
227
241
genericLabels ,
228
242
prometheus.Labels {},
229
243
),
244
+ ThermostatBatteryChargeLevel : prometheus .NewDesc (
245
+ "fritzbox_thermostat_battery_charge_level" ,
246
+ "Battery charge level in percent" ,
247
+ genericLabels ,
248
+ prometheus.Labels {},
249
+ ),
230
250
ThermostatBatteryLow : prometheus .NewDesc (
231
251
"fritzbox_thermostat_batterylow" ,
232
252
"0 if the battery is OK, 1 if it is running low on capacity (this seems to be very unreliable)" ,
@@ -239,14 +259,40 @@ func NewFritzCollector() *fritzCollector {
239
259
genericLabels ,
240
260
prometheus.Labels {},
241
261
),
262
+ ThermostatTempComfort : prometheus .NewDesc (
263
+ "fritzbox_thermostat_comfort" ,
264
+ "Comfort temperature configured in units of 0.1 °C" ,
265
+ genericLabels ,
266
+ prometheus.Labels {},
267
+ ),
268
+ ThermostatTempGoal : prometheus .NewDesc (
269
+ "fritzbox_thermostat_goal" ,
270
+ "Desired temperature (user controlled) in units of 0.1 °C" ,
271
+ genericLabels ,
272
+ prometheus.Labels {},
273
+ ),
274
+ ThermostatTempSaving : prometheus .NewDesc (
275
+ "fritzbox_thermostat_saving" ,
276
+ "Configured energy saving temperature in units of 0.1 °C" ,
277
+ genericLabels ,
278
+ prometheus.Labels {},
279
+ ),
280
+ ThermostatWindowOpen : prometheus .NewDesc (
281
+ "fritzbox_thermostat_window_open" ,
282
+ "1 if detected an open window (usually turns off heating), 0 if not." ,
283
+ genericLabels ,
284
+ prometheus.Labels {},
285
+ ),
242
286
}
243
287
}
244
288
245
289
// stringToFloatMetric converts a string `val` into a valid float metric
246
- func stringToFloatMetric (ch chan <- prometheus.Metric , desc * prometheus.Desc , value string , dev * fritz.Device ) error {
290
+ func stringToFloatMetric (ch chan <- prometheus.Metric , desc * prometheus.Desc , value string , dev * fritz.Device , optional bool ) error {
247
291
val , err := strconv .ParseFloat (value , 64 )
248
292
if err != nil {
249
- ch <- prometheus .NewInvalidMetric (desc , err )
293
+ if ! optional {
294
+ ch <- prometheus .NewInvalidMetric (desc , err )
295
+ }
250
296
return err
251
297
}
252
298
ch <- prometheus .MustNewConstMetric (
@@ -259,6 +305,12 @@ func stringToFloatMetric(ch chan<- prometheus.Metric, desc *prometheus.Desc, val
259
305
)
260
306
return nil
261
307
}
308
+ func canStringToFloatMetric (ch chan <- prometheus.Metric , desc * prometheus.Desc , value string , dev * fritz.Device ) error {
309
+ return stringToFloatMetric (ch , desc , value , dev , true )
310
+ }
311
+ func mustStringToFloatMetric (ch chan <- prometheus.Metric , desc * prometheus.Desc , value string , dev * fritz.Device ) error {
312
+ return stringToFloatMetric (ch , desc , value , dev , false )
313
+ }
262
314
263
315
// parseSwitchStrings parses state strings of switches into floats
264
316
func parseSwitchStrings (val string ) (float64 , error ) {
0 commit comments