diff --git a/collector/collector.go b/collector/collector.go index 611346c..5e4c00c 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -17,20 +17,18 @@ type Collector struct { } // NewHueCollector initialization -func NewHueCollector(URL string, username string) (*Collector, error) { +func NewHueCollector(URL string, username string, metricsFile *metric.MetricsFile) (*Collector, error) { hueExporter := hue.Exporter{ BaseURL: URL, Username: username, } - return &Collector{&hueExporter, nil}, nil + return &Collector{&hueExporter, metricsFile.Metrics}, nil } // Describe for prometheus func (collector *Collector) Describe(ch chan<- *prometheus.Desc) { - - collector.metrics = collector.exporter.InitMetrics() collector.initDescAndType() } @@ -52,7 +50,6 @@ func (collector *Collector) Collect(ch chan<- prometheus.Metric) { //Test collector metrics func (collector *Collector) Test() { - collector.metrics = collector.exporter.InitMetrics() collector.initDescAndType() err := collector.collect() @@ -194,10 +191,6 @@ func getLabelValues(labelNames []string, result map[string]interface{}) ([]strin case "false": labelValueString = "0" } - - if labelname != "name" { - labelValue = strings.ToLower(labelValueString) - } labelValues = append(labelValues, labelValueString) } return labelValues, nil diff --git a/hue/hue.go b/hue/hue.go index ffb1e8f..59700bd 100644 --- a/hue/hue.go +++ b/hue/hue.go @@ -21,116 +21,47 @@ type collectEntry struct { } const ( - TypeLight = "light" - TypeSensor = "sensor" + typeLight = "light" + typeSensor = "sensor" - LabelName = "name" - LabelType = "type" - LabelID = "id" - LabelModelID = "model_id" - LabelManufacturerName = "manufacturer_name" - LabelSwVersion = "sw_version" - LabelSwConfigID = "sw_config_id" - LabelUniqueID = "unique_id" + labelName = "name" + labelType = "type" + labelID = "id" + labelModelID = "model_id" + labelManufacturerName = "manufacturer_name" + labelSwVersion = "sw_version" + labelSwConfigID = "sw_config_id" + labelUniqueID = "unique_id" - LabelStateOn = "state_on" - LabelStateAlert = "state_alert" - LabelStateBri = "state_bri" - LabelStateColorMode = "state_color_mode" - LabelStateCT = "state_ct" - LabelStateReachable = "state_reachable" - LabelStateSaturation = "state_saturation" - LabelStateButtonEvent = "state_buttonevent" - LabelStateDaylight = "state_daylight" - LabelStateLastUpdated = "state_lastupdated" - LabelStateTemperature = "state_temperature" - LabelStateLightLevel = "state_lightlevel" + labelStateOn = "state_on" + labelStateAlert = "state_alert" + labelStateBri = "state_bri" + labelStateColorMode = "state_color_mode" + labelStateCT = "state_ct" + labelStateReachable = "state_reachable" + labelStateSaturation = "state_saturation" + labelStateButtonEvent = "state_buttonevent" + labelStateDaylight = "state_daylight" + labelStateLastUpdated = "state_lastupdated" + labelStateTemperature = "state_temperature" + labelStateLightLevel = "state_lightlevel" - LabelConfigBattery = "config_battery" - LabelConfigOn = "config_on" - LabelConfigReachable = "config_reachable" + labelConfigBattery = "config_battery" + labelConfigOn = "config_on" + labelConfigReachable = "config_reachable" + + labelAPIVersion = "api_version" + labelBridgeID = "bridge_id" + labelIPAddress = "ip_address" + labelInternetServiceInternet = "internetservice_internet" + labelInternetServiceRemoteAccess = "internetservice_remoteaccess" + labelInternetServiceSwUpdate = "internetservice_swupdate" + labelInternetServiceTime = "internetservice_time" + labelLocalTime = "local_time" + labelSwUpdate2LastChange = "sw_update_last_change" + labelZigbeeChannel = "zigbee_channel" ) -// InitMetrics func -func (exporter *Exporter) InitMetrics() (metrics []*metric.Metric) { - - metrics = append(metrics, &metric.Metric{ - HueType: TypeLight, - FqName: "hue_light_info", - Help: "Non-numeric data, value is always 1", - Labels: []string{ - LabelName, - LabelID, - LabelType, - LabelModelID, - LabelManufacturerName, - LabelSwVersion, - LabelSwConfigID, - LabelUniqueID, - LabelStateOn, - LabelStateAlert, - LabelStateBri, - LabelStateCT, - LabelStateReachable, - LabelStateSaturation, - }, - }) - - metrics = append(metrics, &metric.Metric{ - HueType: TypeLight, - FqName: "hue_light_state", - Help: "light status (1=ON, 0=OFF)", - Labels: []string{ - LabelName, - }, - ResultKey: LabelStateOn, - }) - - metrics = append(metrics, &metric.Metric{ - HueType: TypeSensor, - FqName: "hue_sensor_info", - Help: "Non-numeric data, value is always 1", - Labels: []string{ - LabelName, - LabelID, - LabelType, - LabelModelID, - LabelManufacturerName, - LabelSwVersion, - LabelUniqueID, - LabelStateButtonEvent, - LabelStateDaylight, - LabelStateLastUpdated, - LabelStateTemperature, - LabelConfigBattery, - LabelConfigOn, - LabelConfigReachable, - }, - }) - - metrics = append(metrics, &metric.Metric{ - HueType: TypeSensor, - FqName: "hue_sensor_temperature", - Help: "temperature level celsius degree", - Labels: []string{ - LabelName, - }, - ResultKey: LabelStateTemperature, - }) - - metrics = append(metrics, &metric.Metric{ - HueType: TypeSensor, - FqName: "hue_sensor_lightlevel", - Help: "light level", - Labels: []string{ - LabelName, - }, - ResultKey: LabelStateLightLevel, - }) - - return metrics -} - // CollectAll available hue metrics func CollectAll(url string, username string, fileName string) { @@ -150,17 +81,20 @@ func CollectAll(url string, username string, fileName string) { return } + bridgeData, err := collectBridgeInfo(bridge) + if err != nil { + fmt.Sprintln(err) + return + } + for _, sensor := range sensorData { - jsonContent = append(jsonContent, collectEntry{ - Type: "sensor", - Result: sensor, - }) + jsonContent = append(jsonContent, collectEntry{Type: "sensor", Result: sensor}) } for _, light := range lightData { - jsonContent = append(jsonContent, collectEntry{ - Type: "light", - Result: light, - }) + jsonContent = append(jsonContent, collectEntry{Type: "light", Result: light}) + } + for _, bridge := range bridgeData { + jsonContent = append(jsonContent, collectEntry{Type: "bridge", Result: bridge}) } jsonString, err := json.MarshalIndent(jsonContent, "", "\t") @@ -195,11 +129,14 @@ func (exporter *Exporter) Collect(metrics []*metric.Metric) (err error) { for _, metric := range metrics { switch metric.HueType { - case TypeLight: + case typeLight: metric.MetricResult = lightData - case TypeSensor: + case typeSensor: metric.MetricResult = sensorData + default: + return fmt.Errorf("Type '%v' currently not supported", metric.HueType) } + } return nil @@ -209,17 +146,17 @@ func collectSensors(bridge *hueAPI.Bridge) (sensorData []map[string]interface{}, sensors, err := bridge.GetSensors() if err != nil { - return nil, fmt.Errorf("[error GetAllSensors()] '%v'", err) + return nil, fmt.Errorf("[GetAllSensors()] '%v'", err) } for _, sensor := range sensors { result := make(map[string]interface{}) - result[LabelName] = sensor.Name - result[LabelID] = sensor.ID - result[LabelType] = sensor.Type - result[LabelModelID] = sensor.ModelID - result[LabelManufacturerName] = sensor.ManufacturerName - result[LabelSwVersion] = sensor.SwVersion - result[LabelUniqueID] = sensor.UniqueID + result[labelName] = sensor.Name + result[labelID] = sensor.ID + result[labelType] = sensor.Type + result[labelModelID] = sensor.ModelID + result[labelManufacturerName] = sensor.ManufacturerName + result[labelSwVersion] = sensor.SwVersion + result[labelUniqueID] = sensor.UniqueID //State for stateKey, stateValue := range sensor.State { @@ -240,31 +177,56 @@ func collectLights(bridge *hueAPI.Bridge) (lightData []map[string]interface{}, e lights, err := bridge.GetLights() if err != nil { - return nil, fmt.Errorf("[error GetAllLights()] '%v'", err) + return nil, fmt.Errorf("[GetAllLights()] '%v'", err) } for _, light := range lights { result := make(map[string]interface{}) - result[LabelName] = light.Name - result[LabelID] = light.ID - result[LabelType] = light.Type - result[LabelModelID] = light.ModelID - result[LabelManufacturerName] = light.ManufacturerName - result[LabelSwVersion] = light.SwVersion - result[LabelSwConfigID] = light.SwConfigID - result[LabelUniqueID] = light.UniqueID + result[labelName] = light.Name + result[labelID] = light.ID + result[labelType] = light.Type + result[labelModelID] = light.ModelID + result[labelManufacturerName] = light.ManufacturerName + result[labelSwVersion] = light.SwVersion + result[labelSwConfigID] = light.SwConfigID + result[labelUniqueID] = light.UniqueID // State - result[LabelStateOn] = light.State.On - result[LabelStateAlert] = light.State.Alert - result[LabelStateBri] = light.State.Bri - result[LabelStateColorMode] = light.State.ColorMode - result[LabelStateCT] = light.State.Ct - result[LabelStateReachable] = light.State.Reachable - result[LabelStateSaturation] = light.State.Sat + result[labelStateOn] = light.State.On + result[labelStateAlert] = light.State.Alert + result[labelStateBri] = light.State.Bri + result[labelStateColorMode] = light.State.ColorMode + result[labelStateCT] = light.State.Ct + result[labelStateReachable] = light.State.Reachable + result[labelStateSaturation] = light.State.Sat lightData = append(lightData, result) } return lightData, nil } + +func collectBridgeInfo(bridge *hueAPI.Bridge) (bridgeData []map[string]interface{}, err error) { + + config, err := bridge.GetConfig() + if err != nil { + return nil, fmt.Errorf("[GetConfig] '%v'", err) + } + result := make(map[string]interface{}) + result[labelName] = config.Name + result[labelAPIVersion] = config.APIVersion + result[labelBridgeID] = config.BridgeID + result[labelIPAddress] = config.IPAddress + result[labelInternetServiceInternet] = config.InternetService.Internet + result[labelInternetServiceRemoteAccess] = config.InternetService.RemoteAccess + result[labelInternetServiceSwUpdate] = config.InternetService.SwUpdate + result[labelInternetServiceTime] = config.InternetService.Time + result[labelLocalTime] = config.LocalTime + result[labelModelID] = config.ModelID + result[labelSwVersion] = config.SwVersion + result[labelSwUpdate2LastChange] = config.SwUpdate2.LastChange + result[labelZigbeeChannel] = config.ZigbeeChannel + + bridgeData = append(bridgeData, result) + return bridgeData, nil +} diff --git a/main.go b/main.go index d4158f1..88709ff 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,9 @@ package main import ( + "encoding/json" "fmt" + "io/ioutil" "log" "net/http" @@ -11,12 +13,14 @@ import ( "github.com/aexel90/hue_exporter/collector" "github.com/aexel90/hue_exporter/hue" + "github.com/aexel90/hue_exporter/metric" ) var ( - flagBridgeURL = flag.String("hue-url", "", "The URL of the bridge") - 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.") + flagBridgeURL = flag.String("hue-url", "", "The URL of the bridge") + 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.") + flagMetricsFile = flag.String("metrics-file", "metrics.json", "The JSON file with the metric definitions.") flagTest = flag.Bool("test", false, "test configured metrics") flagCollect = flag.Bool("collect", false, "test configured metrics") @@ -27,13 +31,21 @@ func main() { flag.Parse() + var metricsFile *metric.MetricsFile + // collect mode if *flagCollect { hue.CollectAll(*flagBridgeURL, *flagUsername, *flagCollectFile) return } - hueCollector, err := collector.NewHueCollector(*flagBridgeURL, *flagUsername) + err := readAndParseFile(*flagMetricsFile, &metricsFile) + if err != nil { + fmt.Println(err) + return + } + + hueCollector, err := collector.NewHueCollector(*flagBridgeURL, *flagUsername, metricsFile) if err != nil { fmt.Println(err) return @@ -48,3 +60,16 @@ func main() { log.Fatal(http.ListenAndServe(*flagAddress, nil)) } } + +func readAndParseFile(file string, v interface{}) error { + jsonData, err := ioutil.ReadFile(file) + if err != nil { + return fmt.Errorf("error reading metric file: %v", err) + } + + err = json.Unmarshal(jsonData, v) + if err != nil { + return fmt.Errorf("error parsing JSON: %v", err) + } + return nil +} diff --git a/metric/metric.go b/metric/metric.go index 209a4dc..5142c83 100644 --- a/metric/metric.go +++ b/metric/metric.go @@ -14,14 +14,20 @@ type PrometheusResult struct { // Metric struct type Metric struct { - HueType string - Labels []string + HueType string `json:"type"` + ResultKey string `json:"resultKey"` + FqName string `json:"fqName"` + Help string `json:"help"` + Labels []string `json:"labels"` + MetricResult []map[string]interface{} - ResultKey string - FqName string - Help string PromType prometheus.ValueType PromDesc *prometheus.Desc PromResult []*PrometheusResult } + +// MetricsFile struct +type MetricsFile struct { + Metrics []*Metric `json:"metrics"` +} diff --git a/metrics.json b/metrics.json new file mode 100644 index 0000000..aa3c7cd --- /dev/null +++ b/metrics.json @@ -0,0 +1,95 @@ +{ + "metrics": [ + { + "type": "light", + "fqname": "hue_light_info", + "help": "Non-numeric data, value is always 1", + "labels": [ + "id", + "manufacturer_name", + "model_id", + "name", + "state_alert", + "state_bri", + "state_color_mode", + "state_ct", + "state_on", + "state_reachable", + "state_saturation", + "sw_config_id", + "sw_version", + "type", + "unique_id" + ] + }, + { + "type": "light", + "fqname": "hue_light_state", + "help": "light status (1=ON, 0=OFF)", + "resultKey": "state_on", + "labels": [ + "name" + ] + }, + { + "type": "sesnsor", + "fqname": "hue_sensor_info", + "help": "Non-numeric data, value is always 1", + "labels": [ + "config_battery", + "config_on", + "config_reachable", + "id", + "manufacturer_name", + "model_id", + "name", + "state_buttonevent", + "state_daylight", + "state_lastupdated", + "state_lightlevel", + "state_temperature", + "sw_version", + "type", + "unique_id" + ] + }, + { + "type": "sensor", + "fqname": "hue_sensor_temperature", + "help": "temperature level celsius degree", + "resultKey": "state_temperature", + "labels": [ + "name" + ] + }, + { + "type": "sensor", + "fqname": "hue_sensor_lightlevel", + "help": "light level", + "resultKey": "state_lightlevel", + "labels": [ + "name" + ] + }, + { + "type": "bridge", + "fqname": "hue_brdige_info", + "help": "Non-numeric data, value is always 1", + "labels": [ + "api_version", + "bridge_id", + "internetservice_internet", + "internetservice_remoteaccess", + "internetservice_swupdate", + "internetservice_time", + "ip_address", + "local_time", + "model_id", + "name", + "sw_update_last_change", + "sw_version", + "zigbee_channel" + ] + } + ] +} \ No newline at end of file