exporter-go-mystrom/pkg/mystrom/mystrom.go

215 lines
5.2 KiB
Go
Raw Permalink Normal View History

2021-12-23 16:36:04 +01:00
package mystrom
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/prometheus/common/log"
2021-12-23 16:36:04 +01:00
"github.com/prometheus/client_golang/prometheus"
)
const namespace = "mystrom"
// 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"`
Relay bool `json:"relay"`
Temperature float64 `json:"temperature"`
2021-12-23 16:36:04 +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
switchType float64
2021-12-23 16:36:04 +01:00
}
// NewExporter --
func NewExporter(switchIP string) *Exporter {
2021-12-23 16:36:04 +01:00
return &Exporter{
myStromSwitchIp: switchIP,
2021-12-23 16:36:04 +01:00
}
}
// Scrape --
func (e *Exporter) Scrape() (prometheus.Gatherer, error) {
reg := prometheus.NewRegistry()
2021-12-23 16:36:04 +01:00
// --
bodyInfo, err := e.fetchData("/api/v1/info")
if err != nil {
}
2021-12-23 16:36:04 +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
// --
bodyData, err := e.fetchData("/report")
if err != nil {
}
2021-12-23 16:36:04 +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{
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 {
// 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 {
// 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 {
// 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
}
return body, nil
}
2021-12-23 16:36:04 +01:00
// registerMetrics --
func registerMetrics(reg prometheus.Registerer, data switchReport, target string, st float64) error {
2021-12-23 16:36:04 +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"})
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 {
collectorRelay.WithLabelValues(target).Set(0)
2021-12-23 16:36:04 +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"})
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"})
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:36:04 +01:00
return nil
2021-12-23 16:36:04 +01:00
}
// registerMetrics --
func registerInfoMetrics(reg prometheus.Registerer, data switchInfo, target string) error {
2021-12-23 16:36:04 +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:50:40 +01:00
[]string{"instance", "version", "mac", "type", "ssid"})
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
}