more generic metric

This commit is contained in:
aexel90 2020-12-28 17:29:44 +01:00
parent f351bc49de
commit 7c629a62f7
4 changed files with 189 additions and 91 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
.vscode/launch.json .vscode/launch.json
collect.json

View file

@ -181,16 +181,24 @@ func getLabelValues(labelNames []string, result map[string]interface{}) ([]strin
labelValues := []string{} labelValues := []string{}
for _, labelname := range labelNames { for _, labelname := range labelNames {
labelValue := fmt.Sprintf("%v", result[labelname])
if labelValue == "true" { labelValue := result[labelname]
labelValue = "1" if labelValue == nil {
} else if labelValue == "false" { labelValue = ""
labelValue = "0"
} }
if labelname != "Name" {
labelValue = strings.ToLower(labelValue) labelValueString := fmt.Sprintf("%v", labelValue)
switch labelValueString {
case "true":
labelValueString = "1"
case "false":
labelValueString = "0"
} }
labelValues = append(labelValues, labelValue)
if labelname != "name" {
labelValue = strings.ToLower(labelValueString)
}
labelValues = append(labelValues, labelValueString)
} }
return labelValues, nil return labelValues, nil
} }

View file

@ -1,11 +1,12 @@
package hue package hue
import ( import (
"encoding/json"
"fmt" "fmt"
"log" "io/ioutil"
"github.com/aexel90/hue_exporter/metric" "github.com/aexel90/hue_exporter/metric"
hue "github.com/shamx9ir/gohue" // github.com/collinux/gohue hueAPI "github.com/amimof/huego"
) )
// Exporter data // Exporter data
@ -14,31 +15,40 @@ type Exporter struct {
Username string Username string
} }
type collectEntry struct {
Type string `json:"type"`
Result map[string]interface{} `json:"result"`
}
const ( const (
TypeLight = "light" TypeLight = "light"
TypeSensor = "sensor" TypeSensor = "sensor"
LabelName = "Name" LabelName = "name"
LabelType = "Type" LabelType = "type"
LabelIndex = "Index" LabelID = "id"
LabelModelID = "Model_ID" LabelModelID = "model_id"
LabelManufacturerName = "Manufacturer_Name" LabelManufacturerName = "manufacturer_name"
LabelSWVersion = "SW_Version" LabelSwVersion = "sw_version"
LabelUniqueID = "Unique_ID" LabelSwConfigID = "sw_config_id"
LabelStateOn = "State_On" LabelUniqueID = "unique_id"
LabelStateAlert = "State_Alert"
LabelStateBri = "State_Bri" LabelStateOn = "state_on"
LabelStateCT = "State_CT" LabelStateAlert = "state_alert"
LabelStateReachable = "State_Reachable" LabelStateBri = "state_bri"
LabelStateSaturation = "State_Saturation" LabelStateColorMode = "state_color_mode"
LabelStateButtonEvent = "State_Button_Event" LabelStateCT = "state_ct"
LabelStateDaylight = "State_Daylight" LabelStateReachable = "state_reachable"
LabelStateLastUpdated = "State_Last_Updated" LabelStateSaturation = "state_saturation"
LabelStateLastUpdatedTime = "State_Last_Updated_Time" LabelStateButtonEvent = "state_buttonevent"
LabelStateTemperature = "State_Temperature" LabelStateDaylight = "state_daylight"
LabelConfigBattery = "Config_Battery" LabelStateLastUpdated = "state_lastupdated"
LabelConfigOn = "Config_On" LabelStateTemperature = "state_temperature"
LabelConfigReachable = "Config_Reachable" LabelStateLightLevel = "state_lightlevel"
LabelConfigBattery = "config_battery"
LabelConfigOn = "config_on"
LabelConfigReachable = "config_reachable"
) )
// InitMetrics func // InitMetrics func
@ -48,14 +58,31 @@ func (exporter *Exporter) InitMetrics() (metrics []*metric.Metric) {
HueType: TypeLight, HueType: TypeLight,
FqName: "hue_light_info", FqName: "hue_light_info",
Help: "Non-numeric data, value is always 1", Help: "Non-numeric data, value is always 1",
Labels: []string{LabelName, LabelIndex, LabelType, LabelModelID, LabelManufacturerName, LabelSWVersion, LabelUniqueID, LabelStateOn, LabelStateAlert, LabelStateBri, LabelStateCT, LabelStateReachable, LabelStateSaturation}, Labels: []string{
LabelName,
LabelID,
LabelType,
LabelModelID,
LabelManufacturerName,
LabelSwVersion,
LabelSwConfigID,
LabelUniqueID,
LabelStateOn,
LabelStateAlert,
LabelStateBri,
LabelStateCT,
LabelStateReachable,
LabelStateSaturation,
},
}) })
metrics = append(metrics, &metric.Metric{ metrics = append(metrics, &metric.Metric{
HueType: TypeLight, HueType: TypeLight,
FqName: "hue_light_state", FqName: "hue_light_state",
Help: "light status (1=ON, 0=OFF)", Help: "light status (1=ON, 0=OFF)",
Labels: []string{LabelName, LabelIndex, LabelType, LabelModelID, LabelManufacturerName, LabelSWVersion, LabelUniqueID, LabelStateOn}, Labels: []string{
LabelName,
},
ResultKey: LabelStateOn, ResultKey: LabelStateOn,
}) })
@ -63,37 +90,98 @@ func (exporter *Exporter) InitMetrics() (metrics []*metric.Metric) {
HueType: TypeSensor, HueType: TypeSensor,
FqName: "hue_sensor_info", FqName: "hue_sensor_info",
Help: "Non-numeric data, value is always 1", Help: "Non-numeric data, value is always 1",
Labels: []string{LabelName, LabelIndex, LabelType, LabelModelID, LabelManufacturerName, LabelSWVersion, LabelUniqueID, LabelStateButtonEvent, LabelStateDaylight, LabelStateLastUpdated, LabelStateLastUpdatedTime, LabelStateTemperature, LabelConfigBattery, LabelConfigOn, LabelConfigReachable}, Labels: []string{
}) LabelName,
LabelID,
metrics = append(metrics, &metric.Metric{ LabelType,
HueType: TypeSensor, LabelModelID,
FqName: "hue_sensor_battery", LabelManufacturerName,
Help: "battery level percentage", LabelSwVersion,
Labels: []string{LabelName, LabelIndex, LabelType, LabelModelID, LabelManufacturerName, LabelSWVersion, LabelUniqueID}, LabelUniqueID,
ResultKey: LabelConfigBattery, LabelStateButtonEvent,
LabelStateDaylight,
LabelStateLastUpdated,
LabelStateTemperature,
LabelConfigBattery,
LabelConfigOn,
LabelConfigReachable,
},
}) })
metrics = append(metrics, &metric.Metric{ metrics = append(metrics, &metric.Metric{
HueType: TypeSensor, HueType: TypeSensor,
FqName: "hue_sensor_temperature", FqName: "hue_sensor_temperature",
Help: "temperature level celsius degree", Help: "temperature level celsius degree",
Labels: []string{LabelName, LabelIndex, LabelType, LabelModelID, LabelManufacturerName, LabelSWVersion, LabelUniqueID}, Labels: []string{
LabelName,
},
ResultKey: LabelStateTemperature, ResultKey: LabelStateTemperature,
}) })
metrics = append(metrics, &metric.Metric{
HueType: TypeSensor,
FqName: "hue_sensor_lightlevel",
Help: "light level",
Labels: []string{
LabelName,
},
ResultKey: LabelStateLightLevel,
})
return metrics return metrics
} }
// CollectAll available hue metrics
func CollectAll(url string, username string, fileName string) {
bridge := hueAPI.New(url, username)
jsonContent := []collectEntry{}
sensorData, err := collectSensors(bridge)
if err != nil {
fmt.Sprintln(err)
return
}
lightData, err := collectLights(bridge)
if err != nil {
fmt.Sprintln(err)
return
}
for _, sensor := range sensorData {
jsonContent = append(jsonContent, collectEntry{
Type: "sensor",
Result: sensor,
})
}
for _, light := range lightData {
jsonContent = append(jsonContent, collectEntry{
Type: "light",
Result: light,
})
}
jsonString, err := json.MarshalIndent(jsonContent, "", "\t")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(jsonString))
if fileName != "" {
err = ioutil.WriteFile(fileName, jsonString, 0644)
if err != nil {
fmt.Printf("Failed writing JSON file '%s': %s\n", fileName, err.Error())
}
}
}
// Collect metrics // Collect metrics
func (exporter *Exporter) Collect(metrics []*metric.Metric) (err error) { func (exporter *Exporter) Collect(metrics []*metric.Metric) (err error) {
bridge := newBridge(exporter.BaseURL) bridge := hueAPI.New(exporter.BaseURL, exporter.Username)
err = bridge.Login(exporter.Username)
if err != nil {
return fmt.Errorf("[error Login()] '%v'", err)
}
sensorData, err := collectSensors(bridge) sensorData, err := collectSensors(bridge)
if err != nil { if err != nil {
@ -117,31 +205,30 @@ func (exporter *Exporter) Collect(metrics []*metric.Metric) (err error) {
return nil return nil
} }
func collectSensors(bridge *hue.Bridge) (sensorData []map[string]interface{}, err error) { func collectSensors(bridge *hueAPI.Bridge) (sensorData []map[string]interface{}, err error) {
sensors, err := bridge.GetAllSensors() sensors, err := bridge.GetSensors()
if err != nil { if err != nil {
return nil, fmt.Errorf("[error GetAllSensors()] '%v'", err) return nil, fmt.Errorf("[error GetAllSensors()] '%v'", err)
} }
for _, sensor := range sensors { for _, sensor := range sensors {
result := make(map[string]interface{}) result := make(map[string]interface{})
result[LabelName] = sensor.Name result[LabelName] = sensor.Name
result[LabelIndex] = sensor.Index result[LabelID] = sensor.ID
result[LabelType] = sensor.Type result[LabelType] = sensor.Type
result[LabelModelID] = sensor.ModelID result[LabelModelID] = sensor.ModelID
result[LabelManufacturerName] = sensor.ManufacturerName result[LabelManufacturerName] = sensor.ManufacturerName
result[LabelSWVersion] = sensor.SWVersion result[LabelSwVersion] = sensor.SwVersion
result[LabelUniqueID] = sensor.UniqueID result[LabelUniqueID] = sensor.UniqueID
result[LabelStateButtonEvent] = float64(sensor.State.ButtonEvent)
result[LabelStateDaylight] = sensor.State.Daylight
result[LabelStateLastUpdated] = sensor.State.LastUpdated
result[LabelStateLastUpdatedTime] = sensor.State.LastUpdated.Time
result[LabelConfigBattery] = sensor.Config.Battery
result[LabelConfigOn] = sensor.Config.On
result[LabelConfigReachable] = sensor.Config.Reachable
if sensor.Type == "ZLLTemperature" { //State
result[LabelStateTemperature] = float64(sensor.State.Temperature) for stateKey, stateValue := range sensor.State {
result["state_"+stateKey] = stateValue
}
//Config
for stateKey, stateValue := range sensor.Config {
result["config_"+stateKey] = stateValue
} }
sensorData = append(sensorData, result) sensorData = append(sensorData, result)
@ -149,9 +236,9 @@ func collectSensors(bridge *hue.Bridge) (sensorData []map[string]interface{}, er
return sensorData, nil return sensorData, nil
} }
func collectLights(bridge *hue.Bridge) (lightData []map[string]interface{}, err error) { func collectLights(bridge *hueAPI.Bridge) (lightData []map[string]interface{}, err error) {
lights, err := bridge.GetAllLights() lights, err := bridge.GetLights()
if err != nil { if err != nil {
return nil, fmt.Errorf("[error GetAllLights()] '%v'", err) return nil, fmt.Errorf("[error GetAllLights()] '%v'", err)
} }
@ -160,28 +247,24 @@ func collectLights(bridge *hue.Bridge) (lightData []map[string]interface{}, err
result := make(map[string]interface{}) result := make(map[string]interface{})
result[LabelName] = light.Name result[LabelName] = light.Name
result[LabelIndex] = light.Index result[LabelID] = light.ID
result[LabelType] = light.Type result[LabelType] = light.Type
result[LabelModelID] = light.ModelID result[LabelModelID] = light.ModelID
result[LabelManufacturerName] = light.ManufacturerName result[LabelManufacturerName] = light.ManufacturerName
result[LabelSWVersion] = light.SWVersion result[LabelSwVersion] = light.SwVersion
result[LabelSwConfigID] = light.SwConfigID
result[LabelUniqueID] = light.UniqueID result[LabelUniqueID] = light.UniqueID
// State
result[LabelStateOn] = light.State.On result[LabelStateOn] = light.State.On
result[LabelStateAlert] = light.State.Alert result[LabelStateAlert] = light.State.Alert
result[LabelStateBri] = light.State.Bri result[LabelStateBri] = light.State.Bri
result[LabelStateCT] = light.State.CT result[LabelStateColorMode] = light.State.ColorMode
result[LabelStateCT] = light.State.Ct
result[LabelStateReachable] = light.State.Reachable result[LabelStateReachable] = light.State.Reachable
result[LabelStateSaturation] = light.State.Saturation result[LabelStateSaturation] = light.State.Sat
lightData = append(lightData, result) lightData = append(lightData, result)
} }
return lightData, nil return lightData, nil
} }
func newBridge(ipAddr string) *hue.Bridge {
bridge, err := hue.NewBridge(ipAddr)
if err != nil {
log.Fatalf("Error connecting to Hue bridge with '%v': '%v'\n", ipAddr, err)
}
return bridge
}

16
main.go
View file

@ -10,6 +10,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/aexel90/hue_exporter/collector" "github.com/aexel90/hue_exporter/collector"
"github.com/aexel90/hue_exporter/hue"
) )
var ( var (
@ -18,27 +19,32 @@ var (
flagAddress = flag.String("listen-address", "127.0.0.1:9773", "The address to listen on for HTTP requests.") flagAddress = flag.String("listen-address", "127.0.0.1:9773", "The address to listen on for HTTP requests.")
flagTest = flag.Bool("test", false, "test configured metrics") flagTest = flag.Bool("test", false, "test configured metrics")
flagCollect = flag.Bool("collect", false, "test configured metrics")
flagCollectFile = flag.String("collect-file", "", "The JSON file where to store collect results")
) )
func main() { func main() {
flag.Parse() flag.Parse()
// collect mode
if *flagCollect {
hue.CollectAll(*flagBridgeURL, *flagUsername, *flagCollectFile)
return
}
hueCollector, err := collector.NewHueCollector(*flagBridgeURL, *flagUsername) hueCollector, err := collector.NewHueCollector(*flagBridgeURL, *flagUsername)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
// test mode
if *flagTest { if *flagTest {
hueCollector.Test() hueCollector.Test()
return } else {
}
prometheus.MustRegister(hueCollector) prometheus.MustRegister(hueCollector)
http.Handle("/metrics", promhttp.Handler()) http.Handle("/metrics", promhttp.Handler())
fmt.Printf("metrics available at http://%s/metrics\n", *flagAddress) fmt.Printf("metrics available at http://%s/metrics\n", *flagAddress)
log.Fatal(http.ListenAndServe(*flagAddress, nil)) log.Fatal(http.ListenAndServe(*flagAddress, nil))
}
} }