177 lines
4.1 KiB
Go
177 lines
4.1 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"flag"
|
||
|
"fmt"
|
||
|
"github.com/prometheus/client_golang/prometheus"
|
||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type switchReport struct {
|
||
|
Power float64 `json:"power"`
|
||
|
WattPerSec float64 `json:"Ws"`
|
||
|
Relay bool `json:relay`
|
||
|
Temperature float64 `json:"temperature`
|
||
|
}
|
||
|
|
||
|
const namespace = "mystrom"
|
||
|
|
||
|
var (
|
||
|
listenAddress = flag.String("web.listen-address", ":9452",
|
||
|
"Address to listen on")
|
||
|
metricsPath = flag.String("web.metrics-path", "/metrics",
|
||
|
"Path under which to expose metrics")
|
||
|
switchIP = flag.String("switch.ip-address", "",
|
||
|
"IP address of the switch you try to monitor")
|
||
|
|
||
|
up = prometheus.NewDesc(
|
||
|
prometheus.BuildFQName(namespace, "", "up"),
|
||
|
"Was the last myStrom query successful.",
|
||
|
nil, nil,
|
||
|
)
|
||
|
myStromPower = prometheus.NewDesc(
|
||
|
prometheus.BuildFQName(namespace, "", "report_power"),
|
||
|
"The current power consumed by devices attached to the switch",
|
||
|
nil, nil,
|
||
|
)
|
||
|
|
||
|
myStromRelay = prometheus.NewDesc(
|
||
|
prometheus.BuildFQName(namespace, "", "report_relay"),
|
||
|
"The current state of the relay (wether or not the relay is currently turned on)",
|
||
|
nil, nil,
|
||
|
)
|
||
|
|
||
|
myStromTemperature = prometheus.NewDesc(
|
||
|
prometheus.BuildFQName(namespace, "", "report_temperatur"),
|
||
|
"The currently measured temperature by the switch. (Might initially be wrong, but will automatically correct itself over the span of a few hours)",
|
||
|
nil, nil,
|
||
|
)
|
||
|
|
||
|
myStromWattPerSec = prometheus.NewDesc(
|
||
|
prometheus.BuildFQName(namespace, "", "report_watt_per_sec"),
|
||
|
"The average of energy consumed per second from last call this request",
|
||
|
nil, nil,
|
||
|
)
|
||
|
)
|
||
|
|
||
|
type Exporter struct {
|
||
|
myStromSwitchIp string
|
||
|
}
|
||
|
|
||
|
func NewExporter(myStromSwitchIp string) *Exporter {
|
||
|
return &Exporter{
|
||
|
myStromSwitchIp: myStromSwitchIp,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
|
||
|
ch <- up
|
||
|
ch <- myStromPower
|
||
|
ch <- myStromRelay
|
||
|
ch <- myStromTemperature
|
||
|
ch <- myStromWattPerSec
|
||
|
}
|
||
|
|
||
|
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
up, prometheus.GaugeValue, 1,
|
||
|
)
|
||
|
|
||
|
e.FetchSwitchMetrics(e.myStromSwitchIp, ch)
|
||
|
}
|
||
|
|
||
|
func (e *Exporter) FetchSwitchMetrics(switchIP string, ch chan<- prometheus.Metric) {
|
||
|
|
||
|
url := "http://" + switchIP + "/report"
|
||
|
|
||
|
switchClient := http.Client{
|
||
|
Timeout: time.Second * 5, // 3 second timeout, might need to be increased
|
||
|
}
|
||
|
|
||
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||
|
if err != nil {
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
up, prometheus.GaugeValue, 0,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
req.Header.Set("User-Agent", "myStrom-exporter")
|
||
|
|
||
|
res, getErr := switchClient.Do(req)
|
||
|
if getErr != nil {
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
up, prometheus.GaugeValue, 0,
|
||
|
)
|
||
|
|
||
|
}
|
||
|
|
||
|
if res.Body != nil {
|
||
|
defer res.Body.Close()
|
||
|
}
|
||
|
|
||
|
body, readErr := ioutil.ReadAll(res.Body)
|
||
|
if readErr != nil {
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
up, prometheus.GaugeValue, 0,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
report := switchReport{}
|
||
|
err = json.Unmarshal(body, &report)
|
||
|
if err != nil {
|
||
|
fmt.Println(err)
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
up, prometheus.GaugeValue, 0,
|
||
|
)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
myStromPower, prometheus.GaugeValue, report.Power,
|
||
|
)
|
||
|
|
||
|
if report.Relay {
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
myStromRelay, prometheus.GaugeValue, 1,
|
||
|
)
|
||
|
} else {
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
myStromRelay, prometheus.GaugeValue, 0,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
myStromWattPerSec, prometheus.GaugeValue, report.WattPerSec,
|
||
|
)
|
||
|
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
myStromTemperature, prometheus.GaugeValue, report.Temperature,
|
||
|
)
|
||
|
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
|
||
|
flag.Parse()
|
||
|
|
||
|
exporter := NewExporter(*switchIP)
|
||
|
prometheus.MustRegister(exporter)
|
||
|
|
||
|
http.Handle(*metricsPath, promhttp.Handler())
|
||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||
|
w.Write([]byte(`<html>
|
||
|
<head><title>myStrom switch report Exporter</title></head>
|
||
|
<body>
|
||
|
<h1>myStrom Exporter</h1>
|
||
|
<p><a href='` + *metricsPath + `'>Metrics</a></p>
|
||
|
</body>
|
||
|
</html>`))
|
||
|
})
|
||
|
log.Fatal(http.ListenAndServe(*listenAddress, nil))
|
||
|
}
|