2021-12-23 16:36:04 +01:00
package mystrom
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
2021-12-23 16:49:57 +01:00
"github.com/prometheus/common/log"
2021-12-23 16:36:04 +01:00
"github.com/prometheus/client_golang/prometheus"
)
const namespace = "mystrom"
2021-12-23 16:49:57 +01:00
// 5 second timeout, might need to be increased
const reqTimeout = time . Second * 5
2021-12-23 16:36:04 +01:00
type switchReport struct {
Power float64 ` json:"power" `
WattPerSec float64 ` json:"Ws" `
2021-12-23 16:48:29 +01:00
Relay bool ` json:"relay" `
Temperature float64 ` json:"temperature" `
2021-12-23 16:36:04 +01:00
}
2021-12-23 16:49:57 +01:00
type switchInfo struct {
Version string ` json:"version" `
Mac string ` json:"mac" `
SwType float64 ` json:"type" `
SSID string ` json:"ssid" `
Static bool ` json:"static" `
Connected bool ` json:"connected" `
}
// Exporter --
2021-12-23 16:36:04 +01:00
type Exporter struct {
myStromSwitchIp string
2021-12-23 16:49:57 +01:00
switchType float64
2021-12-23 16:36:04 +01:00
}
2021-12-23 16:49:57 +01:00
// NewExporter --
func NewExporter ( switchIP string ) * Exporter {
2021-12-23 16:36:04 +01:00
return & Exporter {
2021-12-23 16:49:57 +01:00
myStromSwitchIp : switchIP ,
2021-12-23 16:36:04 +01:00
}
}
2021-12-23 16:49:57 +01:00
// Scrape --
func ( e * Exporter ) Scrape ( ) ( prometheus . Gatherer , error ) {
reg := prometheus . NewRegistry ( )
2021-12-23 16:36:04 +01:00
2021-12-23 16:49:57 +01:00
// --
bodyInfo , err := e . fetchData ( "/api/v1/info" )
if err != nil {
}
2021-12-23 16:36:04 +01:00
2021-12-23 16:49:57 +01:00
info := switchInfo { }
err = json . Unmarshal ( bodyInfo , & info )
if err != nil {
// fmt.Println(err)
// ch <- prometheus.MustNewConstMetric(
// up, prometheus.GaugeValue, 0,
// )
return reg , fmt . Errorf ( "unable to decode switchReport: %v" , err . Error ( ) )
}
log . Debugf ( "info: %#v" , info )
e . switchType = info . SwType
if err := registerInfoMetrics ( reg , info , e . myStromSwitchIp ) ; err != nil {
return nil , fmt . Errorf ( "failed to register metrics : %v" , err . Error ( ) )
}
2021-12-23 16:36:04 +01:00
2021-12-23 16:49:57 +01:00
// --
bodyData , err := e . fetchData ( "/report" )
if err != nil {
}
2021-12-23 16:36:04 +01:00
2021-12-23 16:49:57 +01:00
report := switchReport { }
err = json . Unmarshal ( bodyData , & report )
if err != nil {
// fmt.Println(err)
// ch <- prometheus.MustNewConstMetric(
// up, prometheus.GaugeValue, 0,
// )
return reg , fmt . Errorf ( "unable to decode switchReport: %v" , err . Error ( ) )
}
log . Debugf ( "report: %#v" , report )
if err := registerMetrics ( reg , report , e . myStromSwitchIp , e . switchType ) ; err != nil {
return nil , fmt . Errorf ( "failed to register metrics : %v" , err . Error ( ) )
}
return reg , nil
}
// fetchData -- get the data from the switch under the given path
func ( e * Exporter ) fetchData ( urlpath string ) ( [ ] byte , error ) {
url := "http://" + e . myStromSwitchIp + urlpath
2021-12-23 16:36:04 +01:00
switchClient := http . Client {
2021-12-23 16:49:57 +01:00
Timeout : reqTimeout ,
Transport : & http . Transport {
DisableCompression : true ,
} ,
2021-12-23 16:36:04 +01:00
}
req , err := http . NewRequest ( http . MethodGet , url , nil )
if err != nil {
2021-12-23 16:49:57 +01:00
// ch <- prometheus.MustNewConstMetric(
// up, prometheus.GaugeValue, 0,
// )
2021-12-23 16:36:04 +01:00
}
req . Header . Set ( "User-Agent" , "myStrom-exporter" )
res , getErr := switchClient . Do ( req )
if getErr != nil {
2021-12-23 16:49:57 +01:00
// ch <- prometheus.MustNewConstMetric(
// up, prometheus.GaugeValue, 0,
// )
2021-12-23 16:36:04 +01:00
}
if res . Body != nil {
defer res . Body . Close ( )
}
body , readErr := ioutil . ReadAll ( res . Body )
if readErr != nil {
2021-12-23 16:49:57 +01:00
// ch <- prometheus.MustNewConstMetric(
// up, prometheus.GaugeValue, 0,
// )
return [ ] byte { } , fmt . Errorf ( "unable to read body: %v" , readErr . Error ( ) )
2021-12-23 16:36:04 +01:00
}
2021-12-23 16:49:57 +01:00
return body , nil
}
2021-12-23 16:36:04 +01:00
2021-12-23 16:49:57 +01:00
// registerMetrics --
func registerMetrics ( reg prometheus . Registerer , data switchReport , target string , st float64 ) error {
2021-12-23 16:36:04 +01:00
2021-12-23 16:49:57 +01:00
// --
collectorRelay := prometheus . NewGaugeVec (
prometheus . GaugeOpts {
Namespace : namespace ,
Name : "relay" ,
Help : "The current state of the relay (wether or not the relay is currently turned on)" ,
} ,
2021-12-23 16:50:40 +01:00
[ ] string { "instance" } )
2021-12-23 16:49:57 +01:00
if err := reg . Register ( collectorRelay ) ; err != nil {
return fmt . Errorf ( "failed to register metric %v: %v" , "relay" , err . Error ( ) )
}
if data . Relay {
collectorRelay . WithLabelValues ( target ) . Set ( 1 )
2021-12-23 16:36:04 +01:00
} else {
2021-12-23 16:49:57 +01:00
collectorRelay . WithLabelValues ( target ) . Set ( 0 )
2021-12-23 16:36:04 +01:00
}
2021-12-23 16:49:57 +01:00
if st != 114 {
// --
collectorPower := prometheus . NewGaugeVec (
prometheus . GaugeOpts {
Namespace : namespace ,
Name : "power" ,
Help : "The current power consumed by devices attached to the switch" ,
} ,
2021-12-23 16:50:40 +01:00
[ ] string { "instance" } )
2021-12-23 16:49:57 +01:00
if err := reg . Register ( collectorPower ) ; err != nil {
return fmt . Errorf ( "failed to register metric %v: %v" , "power" , err . Error ( ) )
}
collectorPower . WithLabelValues ( target ) . Set ( data . Power )
// --
collectorTemperature := prometheus . NewGaugeVec (
prometheus . GaugeOpts {
Namespace : namespace ,
Name : "temperature" ,
Help : "The currently measured temperature by the switch. (Might initially be wrong, but will automatically correct itself over the span of a few hours)" ,
} ,
2021-12-23 16:50:40 +01:00
[ ] string { "instance" } )
2021-12-23 16:49:57 +01:00
if err := reg . Register ( collectorTemperature ) ; err != nil {
return fmt . Errorf ( "failed to register metric %v: %v" , "temperature" , err . Error ( ) )
}
collectorTemperature . WithLabelValues ( target ) . Set ( data . Temperature )
2021-12-23 16:36:04 +01:00
2021-12-23 16:49:57 +01:00
}
2021-12-23 16:36:04 +01:00
2021-12-23 16:49:57 +01:00
return nil
2021-12-23 16:36:04 +01:00
}
2021-12-23 16:49:57 +01:00
// registerMetrics --
func registerInfoMetrics ( reg prometheus . Registerer , data switchInfo , target string ) error {
2021-12-23 16:36:04 +01:00
2021-12-23 16:49:57 +01:00
// --
collectorInfo := prometheus . NewGaugeVec (
prometheus . GaugeOpts {
Namespace : namespace ,
Name : "info" ,
2021-12-23 16:50:40 +01:00
Help : "general information about the device" ,
2021-12-23 16:49:57 +01:00
} ,
2021-12-23 16:50:40 +01:00
[ ] string { "instance" , "version" , "mac" , "type" , "ssid" } )
2021-12-23 16:49:57 +01:00
if err := reg . Register ( collectorInfo ) ; err != nil {
return fmt . Errorf ( "failed to register metric %v: %v" , "info" , err . Error ( ) )
}
collectorInfo . WithLabelValues ( target , data . Version , data . Mac , fmt . Sprintf ( "%v" , data . SwType ) , data . SSID ) . Set ( 1 )
return nil
2021-12-23 16:36:04 +01:00
}