@@ -16,33 +16,43 @@ var (
1616)
1717
1818type 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
3136}
3237
3338func (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
4045 ch <- fc .SwitchState
4146 ch <- fc .SwitchMode
4247 ch <- fc .SwitchBoxLock
4348 ch <- fc .SwitchDeviceLock
49+ ch <- fc .ThermostatBatteryChargeLevel
4450 ch <- fc .ThermostatBatteryLow
4551 ch <- fc .ThermostatErrorCode
52+ ch <- fc .ThermostatTempComfort
53+ ch <- fc .ThermostatTempGoal
54+ ch <- fc .ThermostatTempSaving
55+ ch <- fc .ThermostatWindowOpen
4656}
4757
4858func (fc * fritzCollector ) Collect (ch chan <- prometheus.Metric ) {
@@ -51,24 +61,29 @@ func (fc *fritzCollector) Collect(ch chan<- prometheus.Metric) {
5161
5262 if err != nil {
5363 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 )
6070 ch <- prometheus .NewInvalidMetric (fc .SwitchState , err )
6171 ch <- prometheus .NewInvalidMetric (fc .SwitchMode , err )
6272 ch <- prometheus .NewInvalidMetric (fc .SwitchBoxLock , err )
6373 ch <- prometheus .NewInvalidMetric (fc .SwitchDeviceLock , err )
74+ ch <- prometheus .NewInvalidMetric (fc .ThermostatBatteryChargeLevel , err )
6475 ch <- prometheus .NewInvalidMetric (fc .ThermostatBatteryLow , err )
6576 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 )
6681 return
6782 }
6883
6984 for _ , dev := range l .Devices {
7085 ch <- prometheus .MustNewConstMetric (
71- fc .InfoDesc ,
86+ fc .Info ,
7287 prometheus .GaugeValue ,
7388 1.0 ,
7489 dev .Identifier ,
@@ -81,7 +96,7 @@ func (fc *fritzCollector) Collect(ch chan<- prometheus.Metric) {
8196 )
8297
8398 ch <- prometheus .MustNewConstMetric (
84- fc .PresentDesc ,
99+ fc .Present ,
85100 prometheus .GaugeValue ,
86101 float64 (dev .Present ),
87102 dev .Identifier ,
@@ -90,59 +105,58 @@ func (fc *fritzCollector) Collect(ch chan<- prometheus.Metric) {
90105 )
91106
92107 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 {
94109 log .Printf ("Unable to parse temperature data of \" %s\" : %v\n " , dev .Name , err )
95110 }
96111
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 {
98113 log .Printf ("Unable to parse temperature offset data of \" %s\" : %v\n " , dev .Name , err )
99114 }
100115 }
101116
102117 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 {
104119 log .Printf ("Unable to parse energy data of \" %s\" : %v\n " , dev .Name , err )
105120 }
106121
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 {
108123 log .Printf ("Unable to parse power data of \" %s\" : %v\n " , dev .Name , err )
109124 }
110125 }
111126
112127 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 {
115134 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- )
125135 }
126136
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 )
136155 }
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 )
146160 }
147161 }
148162
@@ -165,39 +179,39 @@ func (fc *fritzCollector) Collect(ch chan<- prometheus.Metric) {
165179
166180func NewFritzCollector () * fritzCollector {
167181 return & fritzCollector {
168- InfoDesc : prometheus .NewDesc (
182+ Info : prometheus .NewDesc (
169183 "fritzbox_device_info" ,
170184 "Device information" ,
171185 append (genericLabels ,
172186 "internal_id" , "fw_version" , "manufacturer" , "functionbitmask" ,
173187 ),
174188 prometheus.Labels {},
175189 ),
176- PresentDesc : prometheus .NewDesc (
190+ Present : prometheus .NewDesc (
177191 "fritzbox_device_present" ,
178192 "Device connected (1) or not (0)" ,
179193 genericLabels ,
180194 prometheus.Labels {},
181195 ),
182- TemperatureDesc : prometheus .NewDesc (
196+ Temperature : prometheus .NewDesc (
183197 "fritzbox_temperature" ,
184198 "Temperature measured at the device sensor in units of 0.1 °C" ,
185199 genericLabels ,
186200 prometheus.Labels {},
187201 ),
188- TemperatureOffsetDesc : prometheus .NewDesc (
202+ TemperatureOffset : prometheus .NewDesc (
189203 "fritzbox_temperature_offset" ,
190204 "Temperature offset (set by the user) in units of 0.1 °C" ,
191205 genericLabels ,
192206 prometheus.Labels {},
193207 ),
194- EnergyWhDesc : prometheus .NewDesc (
208+ EnergyWh : prometheus .NewDesc (
195209 "fritzbox_energy" ,
196210 "Absolute energy consumption (in Wh) since the device started operating" ,
197211 genericLabels ,
198212 prometheus.Labels {},
199213 ),
200- PowerWDesc : prometheus .NewDesc (
214+ PowerW : prometheus .NewDesc (
201215 "fritzbox_power" ,
202216 "Current power (in W), refreshed approx every 2 minutes" ,
203217 genericLabels ,
@@ -227,6 +241,12 @@ func NewFritzCollector() *fritzCollector {
227241 genericLabels ,
228242 prometheus.Labels {},
229243 ),
244+ ThermostatBatteryChargeLevel : prometheus .NewDesc (
245+ "fritzbox_thermostat_battery_charge_level" ,
246+ "Battery charge level in percent" ,
247+ genericLabels ,
248+ prometheus.Labels {},
249+ ),
230250 ThermostatBatteryLow : prometheus .NewDesc (
231251 "fritzbox_thermostat_batterylow" ,
232252 "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 {
239259 genericLabels ,
240260 prometheus.Labels {},
241261 ),
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+ ),
242286 }
243287}
244288
245289// 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 {
247291 val , err := strconv .ParseFloat (value , 64 )
248292 if err != nil {
249- ch <- prometheus .NewInvalidMetric (desc , err )
293+ if ! optional {
294+ ch <- prometheus .NewInvalidMetric (desc , err )
295+ }
250296 return err
251297 }
252298 ch <- prometheus .MustNewConstMetric (
@@ -259,6 +305,12 @@ func stringToFloatMetric(ch chan<- prometheus.Metric, desc *prometheus.Desc, val
259305 )
260306 return nil
261307}
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+ }
262314
263315// parseSwitchStrings parses state strings of switches into floats
264316func parseSwitchStrings (val string ) (float64 , error ) {
0 commit comments