more generic metric
This commit is contained in:
parent
f351bc49de
commit
7c629a62f7
4 changed files with 189 additions and 91 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
|
collect.json
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
231
hue/hue.go
231
hue/hue.go
|
@ -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,
|
||||||
|
LabelType,
|
||||||
|
LabelModelID,
|
||||||
|
LabelManufacturerName,
|
||||||
|
LabelSwVersion,
|
||||||
|
LabelUniqueID,
|
||||||
|
LabelStateButtonEvent,
|
||||||
|
LabelStateDaylight,
|
||||||
|
LabelStateLastUpdated,
|
||||||
|
LabelStateTemperature,
|
||||||
|
LabelConfigBattery,
|
||||||
|
LabelConfigOn,
|
||||||
|
LabelConfigReachable,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
metrics = append(metrics, &metric.Metric{
|
metrics = append(metrics, &metric.Metric{
|
||||||
HueType: TypeSensor,
|
HueType: TypeSensor,
|
||||||
FqName: "hue_sensor_battery",
|
FqName: "hue_sensor_temperature",
|
||||||
Help: "battery level percentage",
|
Help: "temperature level celsius degree",
|
||||||
Labels: []string{LabelName, LabelIndex, LabelType, LabelModelID, LabelManufacturerName, LabelSWVersion, LabelUniqueID},
|
Labels: []string{
|
||||||
ResultKey: LabelConfigBattery,
|
LabelName,
|
||||||
})
|
},
|
||||||
|
|
||||||
metrics = append(metrics, &metric.Metric{
|
|
||||||
HueType: TypeSensor,
|
|
||||||
FqName: "hue_sensor_temperature",
|
|
||||||
Help: "temperature level celsius degree",
|
|
||||||
Labels: []string{LabelName, LabelIndex, LabelType, LabelModelID, LabelManufacturerName, LabelSWVersion, LabelUniqueID},
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
24
main.go
24
main.go
|
@ -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 (
|
||||||
|
@ -17,28 +18,33 @@ var (
|
||||||
flagUsername = flag.String("username", "", "The username token having bridge access")
|
flagUsername = flag.String("username", "", "The username token having bridge access")
|
||||||
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)
|
||||||
|
http.Handle("/metrics", promhttp.Handler())
|
||||||
|
fmt.Printf("metrics available at http://%s/metrics\n", *flagAddress)
|
||||||
|
log.Fatal(http.ListenAndServe(*flagAddress, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
prometheus.MustRegister(hueCollector)
|
|
||||||
|
|
||||||
http.Handle("/metrics", promhttp.Handler())
|
|
||||||
fmt.Printf("metrics available at http://%s/metrics\n", *flagAddress)
|
|
||||||
log.Fatal(http.ListenAndServe(*flagAddress, nil))
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue