diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6e2e83 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +onewire_exporter diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e2d7e36 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Martin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e3bd4d4 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# onewire_exporter + +Prometheus exporter for 1-wire temperature sensors connected to a Raspberry PI + + \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..d8fb1fa --- /dev/null +++ b/main.go @@ -0,0 +1,95 @@ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prometheus/common/log" + yaml "gopkg.in/yaml.v2" +) + +const version string = "0.1" + +type List struct { + Names map[string]string +} + +var ( + showVersion = flag.Bool("version", false, "Print version information.") + listenAddress = flag.String("listen-address", ":9330", "Address on which to expose metrics.") + metricsPath = flag.String("path", "/metrics", "Path under which to expose metrics.") + ignoreUnknown = flag.Bool("ignore", true, "Ignores sensors without a name") + nameFile = flag.String("names", "names.yaml", "File maping IDs to names") + + list List +) + +func init() { + flag.Usage = func() { + fmt.Println("Usage: onewire_exporter [ ... ]\n\nParameters:") + fmt.Println() + flag.PrintDefaults() + } +} + +func main() { + flag.Parse() + + if *showVersion { + printVersion() + os.Exit(0) + } + + filename, _ := filepath.Abs(*nameFile) + yamlFile, err := ioutil.ReadFile(filename) + + if err != nil { + log.Fatal("Can't read names file") + } + + err = yaml.Unmarshal(yamlFile, &list) + if err != nil { + log.Fatal("Can't read names file") + } + + startServer() +} + +func printVersion() { + fmt.Println("onewire_exporter") + fmt.Printf("Version: %s\n", version) +} + +func startServer() { + log.Infof("Starting onewire exporter (Version: %s)\n", version) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(` + onewire Exporter (Version ` + version + `) + +

onewire Exporter

+

Metrics

+

More information:

+

github.com/l3akage/onewire_exporter

+ + `)) + }) + http.HandleFunc(*metricsPath, handleMetricsRequest) + + log.Infof("Listening for %s on %s\n", *metricsPath, *listenAddress) + log.Fatal(http.ListenAndServe(*listenAddress, nil)) +} + +func handleMetricsRequest(w http.ResponseWriter, r *http.Request) { + reg := prometheus.NewRegistry() + reg.MustRegister(&onewireCollector{}) + + promhttp.HandlerFor(reg, promhttp.HandlerOpts{ + ErrorLog: log.NewErrorLogger(), + ErrorHandling: promhttp.ContinueOnError}).ServeHTTP(w, r) +} diff --git a/names.yaml b/names.yaml new file mode 100644 index 0000000..c883eef --- /dev/null +++ b/names.yaml @@ -0,0 +1,2 @@ +names: + id: test device diff --git a/onewire_collector.go b/onewire_collector.go new file mode 100644 index 0000000..8c1c629 --- /dev/null +++ b/onewire_collector.go @@ -0,0 +1,107 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "regexp" + "strconv" + "strings" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/log" +) + +const prefix = "onewire_" + +var ( + upDesc *prometheus.Desc + tempDesc *prometheus.Desc +) + +func init() { + upDesc = prometheus.NewDesc(prefix+"up", "Scrape was successful", nil, nil) + tempDesc = prometheus.NewDesc(prefix+"temp", "Air temperature (in degrees C)", []string{"id", "name"}, nil) +} + +type Temp struct { + ID string + Value float64 +} + +type onewireCollector struct { +} + +func getTemperatures() ([]Temp, error) { + reg, err := regexp.Compile("[^0-9]+") + if err != nil { + log.Fatal(err) + } + devices, err := ioutil.ReadDir("/sys/bus/w1/devices/") + if err != nil { + return nil, err + } + var values []Temp + for _, device := range devices { + if device.Name() == "w1_bus_master1" { + continue + } + content, err := ioutil.ReadFile("/sys/bus/w1/devices/" + device.Name() + "/w1_slave") + if err != nil { + log.Infof("Error reading device %s\n", device.Name()) + continue + } + lines := strings.Split(string(content), "\n") + if len(lines) != 3 { + log.Infof("Unknown format for device %s\n", device.Name()) + continue + } + if !strings.Contains(lines[0], "YES") { + log.Infof("CRC invalid for device %s\n", device.Name()) + continue + } + data := strings.SplitAfter(lines[1], "t=") + if len(data) != 2 { + log.Infof("Temp value not found for device %s\n", device.Name()) + continue + } + strValue := reg.ReplaceAllString(data[1], "") + + tempInt, err := strconv.ParseFloat(strValue, 64) + if err != nil { + continue + } + values = append(values, Temp{ + ID: device.Name(), + Value: tempInt / 1000.0, + }) + } + return values, nil +} + +func (c onewireCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- upDesc + ch <- tempDesc +} + +func (c onewireCollector) Collect(ch chan<- prometheus.Metric) { + values, err := getTemperatures() + if err != nil { + fmt.Fprintln(os.Stderr, "error getting sensor data", err) + ch <- prometheus.MustNewConstMetric(upDesc, prometheus.GaugeValue, 0) + } else { + for _, sensor := range values { + n := list.Names[sensor.ID] + if n == "" { + if *ignoreUnknown == true { + log.Infof("Ingoring unknown device %s\n", sensor.ID) + continue + } else { + n = sensor.ID + } + } + l := []string{sensor.ID, n} + ch <- prometheus.MustNewConstMetric(tempDesc, prometheus.GaugeValue, float64(sensor.Value), l...) + } + } +} diff --git a/onewire_exporter.service b/onewire_exporter.service new file mode 100644 index 0000000..8526eac --- /dev/null +++ b/onewire_exporter.service @@ -0,0 +1,16 @@ +[Unit] +Description=Prometheus onewire_exporter +Wants=basic.target +After=basic.target network.target + +[Service] +User=pi +Group=pi +ExecStart=/home/pi/onewire_exporter -names=/home/pi/names.yaml + +ExecReload=/bin/kill -HUP $MAINPID +KillMode=process +Restart=always + +[Install] +WantedBy=multi-user.target