Skip to content

Commit bdba949

Browse files
authored
Add Prometheus exporter (#1)
* Create prometheus exporter * Minor fixes * Removed all of the device-by-device code and now loop over all devices returned * Rename zoneName
1 parent 8d50cd3 commit bdba949

File tree

4 files changed

+168
-2
lines changed

4 files changed

+168
-2
lines changed

Dockerfile

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# syntax=docker/dockerfile:1
2+
3+
FROM golang:1.23.3
4+
5+
# Set destination for COPY
6+
WORKDIR /app
7+
8+
# Download Go modules
9+
COPY go.mod go.sum ./
10+
RUN go mod download
11+
12+
# Copy the source code. Note the slash at the end, as explained in
13+
# https://docs.docker.com/reference/dockerfile/#copy
14+
COPY *.go ./
15+
RUN mkdir prometheus
16+
COPY prometheus/prometheus.go prometheus
17+
18+
# Build
19+
WORKDIR /app/prometheus
20+
RUN CGO_ENABLED=0 GOOS=linux go build .
21+
22+
# Optional:
23+
# To bind to a TCP port, runtime parameters must be supplied to the docker command.
24+
# But we can document in the Dockerfile what ports
25+
# the application is going to listen on by default.
26+
# https://docs.docker.com/reference/dockerfile/#expose
27+
EXPOSE 9101
28+
29+
# Run
30+
CMD ["./prometheus"]

go.mod

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,19 @@ require (
77
github.com/google/go-querystring v1.1.0
88
)
99

10-
require golang.org/x/net v0.27.0 // indirect
10+
require (
11+
github.com/beorn7/perks v1.0.1 // indirect
12+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
13+
github.com/klauspost/compress v1.17.9 // indirect
14+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
15+
github.com/prometheus/client_model v0.6.1 // indirect
16+
github.com/prometheus/common v0.55.0 // indirect
17+
github.com/prometheus/procfs v0.15.1 // indirect
18+
google.golang.org/protobuf v1.34.2 // indirect
19+
)
20+
21+
require (
22+
github.com/prometheus/client_golang v1.20.5
23+
golang.org/x/net v0.27.0 // indirect
24+
golang.org/x/sys v0.28.0 // indirect
25+
)

go.sum

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
1+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
2+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
3+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
4+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
15
github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
26
github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
3-
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
47
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
8+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
9+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
510
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
611
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
12+
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
13+
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
14+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
15+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
16+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
17+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
18+
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
19+
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
20+
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
21+
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
22+
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
23+
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
24+
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
25+
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
726
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
827
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
28+
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
29+
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
930
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
1031
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
1132
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
33+
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
34+
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=

prometheus/prometheus.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"net/http"
6+
"net/url"
7+
"os"
8+
"strconv"
9+
"strings"
10+
11+
"github.com/prometheus/client_golang/prometheus"
12+
"github.com/prometheus/client_golang/prometheus/promhttp"
13+
14+
"github.com/nerd2/neohubgo"
15+
)
16+
17+
type neoCollector struct {
18+
currentTemp *prometheus.Desc
19+
targetTemp *prometheus.Desc
20+
isHeating *prometheus.Desc
21+
}
22+
23+
func newNeoCollector() *neoCollector {
24+
return &neoCollector{
25+
currentTemp: prometheus.NewDesc("current_temp",
26+
"The current temperature of the zone",
27+
[]string{"zone", "device"}, nil,
28+
),
29+
targetTemp: prometheus.NewDesc("target_temp",
30+
"The target temperature of the zone",
31+
[]string{"zone", "device"}, nil,
32+
),
33+
isHeating: prometheus.NewDesc("is_heating",
34+
"Whether the current zone is heating",
35+
[]string{"zone", "device"}, nil,
36+
),
37+
}
38+
}
39+
40+
func (collector *neoCollector) Describe(ch chan<- *prometheus.Desc) {
41+
42+
ch <- collector.currentTemp
43+
ch <- collector.targetTemp
44+
ch <- collector.isHeating
45+
}
46+
47+
// Collect implements required collect function for all promehteus collectors
48+
func (collector *neoCollector) Collect(ch chan<- prometheus.Metric) {
49+
50+
username := os.Getenv("NEOPROM_USERNAME")
51+
password := os.Getenv("NEOPROM_PASSWORD")
52+
53+
nh := neohubgo.NewNeoHub(&neohubgo.Options{Username: username, Password: password})
54+
devices, err := nh.Login()
55+
if err != nil {
56+
log.Println("Error : couldn't connect -> ", err)
57+
return
58+
}
59+
60+
for _, device := range devices {
61+
if device.Online {
62+
deviceName, _ := url.QueryUnescape(strings.Trim(device.DeviceName, " "))
63+
64+
data, err := nh.GetData(device.DeviceId)
65+
if err != nil {
66+
// If we've got this far, this may be recoverable and we may be able to query other devices
67+
log.Println("Warn : Unable to query device -> ", err)
68+
}
69+
70+
for _, liveDev := range data.CacheValue.LiveInfo.Devices {
71+
actualTemp, _ := strconv.ParseFloat(liveDev.ActualTemp, 64)
72+
targetTemp, _ := strconv.ParseFloat(liveDev.SetTemp, 64)
73+
zoneName, _ := url.QueryUnescape(strings.Trim(liveDev.ZoneName, " "))
74+
75+
var isHeating float64
76+
if liveDev.HeatOn {
77+
isHeating = 1.0
78+
} else {
79+
isHeating = 0.0
80+
}
81+
82+
ch <- prometheus.MustNewConstMetric(collector.currentTemp, prometheus.GaugeValue, actualTemp, zoneName, deviceName)
83+
ch <- prometheus.MustNewConstMetric(collector.targetTemp, prometheus.GaugeValue, targetTemp, zoneName, deviceName)
84+
ch <- prometheus.MustNewConstMetric(collector.isHeating, prometheus.GaugeValue, isHeating, zoneName, deviceName)
85+
86+
}
87+
}
88+
}
89+
}
90+
91+
func main() {
92+
neo := newNeoCollector()
93+
prometheus.MustRegister(neo)
94+
port := "9101"
95+
log.Printf("Starting metrics server on port %s", port)
96+
http.Handle("/metrics", promhttp.Handler())
97+
log.Fatal(http.ListenAndServe(":"+port, nil))
98+
}

0 commit comments

Comments
 (0)