Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ require (
github.com/srebhan/protobufquery v1.0.4
github.com/stretchr/testify v1.11.1
github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62
github.com/tdrn-org/go-hue v0.3.0
github.com/tdrn-org/go-hue v1.2.0
github.com/tdrn-org/go-nsdp v0.5.1
github.com/tdrn-org/go-tr064 v0.3.0
github.com/testcontainers/testcontainers-go v0.42.0
Expand Down Expand Up @@ -374,7 +374,7 @@ require (
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.22.5 // indirect
github.com/go-openapi/jsonpointer v0.23.0 // indirect
github.com/go-openapi/jsonreference v0.21.4 // indirect
github.com/go-openapi/swag v0.26.0 // indirect
github.com/go-openapi/swag/cmdutils v0.26.0 // indirect
Expand Down Expand Up @@ -461,7 +461,7 @@ require (
github.com/likexian/gokit v0.25.16 // indirect
github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mailru/easyjson v0.9.2 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-ieproxy v0.0.11 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand Down Expand Up @@ -496,7 +496,7 @@ require (
github.com/nats-io/nuid v1.0.1 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/ncw/swift/v2 v2.0.3 // indirect
github.com/oapi-codegen/runtime v1.1.1 // indirect
github.com/oapi-codegen/runtime v1.4.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.148.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,8 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
github.com/go-openapi/jsonpointer v0.23.0 h1:c25HFTJ6uWGmoe5BQI6p72p4o7KnlWYsy1MeFlAumsw=
github.com/go-openapi/jsonpointer v0.23.0/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY=
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
github.com/go-openapi/swag v0.26.0 h1:GVDXCmfvhfu1BxiHo8/FA+BbKmhecHnG3varjON5/RI=
Expand Down Expand Up @@ -1855,6 +1857,8 @@ github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M=
github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
Expand Down Expand Up @@ -2034,6 +2038,8 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/oapi-codegen/runtime v1.4.0 h1:KLOSFOp7UzkbS7Cs1ms6NBEKYr0WmH2wZG0KKbd2er4=
github.com/oapi-codegen/runtime v1.4.0/go.mod h1:5sw5fxCDmnOzKNYmkVNF8d34kyUeejJEY8HNT2WaPec=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4=
Expand Down Expand Up @@ -2377,6 +2383,8 @@ github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62 h1:Oj2e7Sae4XrOs
github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62/go.mod h1:qUzPVlSj2UgxJkVbH0ZwuuiR46U8RBMDT5KLY78Ifpw=
github.com/tdrn-org/go-hue v0.3.0 h1:ywIlfTx9lcDp+n9XyGNY/9mUihW82lsWUanTzvdfeMc=
github.com/tdrn-org/go-hue v0.3.0/go.mod h1:KUnPy2lGoP43ygNoCg6jVEhf8h5fpRn0Esjxq9syCnU=
github.com/tdrn-org/go-hue v1.2.0 h1:GjLkbOhDCgK/i/Kp+d9S5gJ8DKSsLk8edpn5PNBHnHs=
github.com/tdrn-org/go-hue v1.2.0/go.mod h1:WW/nt+xwuz7f2XIfe6aMMKzmqN0jXjeSEA6c0F9j6js=
github.com/tdrn-org/go-nsdp v0.5.1 h1:w80Y5zF02nEopj/ycTcsDzeq5DgYeKquvGe8xuWXcEM=
github.com/tdrn-org/go-nsdp v0.5.1/go.mod h1:e2I00mSN+Gl8DBgQmSerI6z4M2KrsQbN0SjpQYoI1OI=
github.com/tdrn-org/go-tr064 v0.3.0 h1:jeab3z69trkwsxNN1Vo3W+ED/u61MbWTZW4PNI2+1pg=
Expand Down
197 changes: 93 additions & 104 deletions plugins/inputs/huebridge/bridge.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package huebridge

import (
"context"
"crypto/tls"
"fmt"
"maps"
Expand Down Expand Up @@ -35,157 +36,147 @@ func (b *bridge) String() string {
return b.url.Redacted()
}

func (b *bridge) process(acc telegraf.Accumulator) error {
func (b *bridge) process(ctx context.Context, acc telegraf.Accumulator) error {
if b.resolvedClient == nil {
if err := b.resolve(); err != nil {
return err
}
}
b.log.Tracef("Processing bridge %s", b)
if err := b.fetchMetadata(); err != nil {
if err := b.fetchMetadata(ctx); err != nil {
// Discard previously resolved client and re-resolve on next process call
b.resolvedClient = nil
return err
}
acc.AddError(b.processLights(acc))
acc.AddError(b.processTemperatures(acc))
acc.AddError(b.processLightLevels(acc))
acc.AddError(b.processMotionSensors(acc))
acc.AddError(b.processDevicePowers(acc))
acc.AddError(b.processLights(ctx, acc))
acc.AddError(b.processTemperatures(ctx, acc))
acc.AddError(b.processLightLevels(ctx, acc))
acc.AddError(b.processMotionSensors(ctx, acc))
acc.AddError(b.processDevicePowers(ctx, acc))
return nil
}

func (b *bridge) processLights(acc telegraf.Accumulator) error {
getLightsResponse, err := b.resolvedClient.GetLights()
func (b *bridge) processLights(ctx context.Context, acc telegraf.Accumulator) error {
getLightsResponse, err := b.resolvedClient.GetLights(ctx)
if err != nil {
return fmt.Errorf("failed to access bridge lights on %s: %w", b, err)
}
if getLightsResponse.HTTPResponse.StatusCode != http.StatusOK {
return fmt.Errorf("failed to fetch bridge lights from %s: %s", b, getLightsResponse.HTTPResponse.Status)
}
responseData := getLightsResponse.JSON200.Data
if responseData != nil {
for _, light := range *responseData {
tags := make(map[string]string)
tags["bridge_id"] = b.resolvedClient.Bridge().BridgeId
tags["room"] = b.resolveResourceRoom(*light.Id, *light.Metadata.Name)
tags["device"] = *light.Metadata.Name
fields := make(map[string]interface{})
if *light.On.On {
fields["on"] = 1
} else {
fields["on"] = 0
}
acc.AddGauge("huebridge_light", fields, tags)
for _, light := range responseData {
tags := make(map[string]string)
tags["bridge_id"] = b.resolvedClient.Bridge().BridgeId
tags["room"] = b.resolveResourceRoom(light.Id, light.Metadata.Name)
tags["device"] = light.Metadata.Name
Comment thread
hdecarne marked this conversation as resolved.
Outdated
fields := make(map[string]interface{})
Comment thread
hdecarne marked this conversation as resolved.
Outdated
if light.On.On {
fields["on"] = 1
} else {
fields["on"] = 0
}
acc.AddGauge("huebridge_light", fields, tags)
}
return nil
}

func (b *bridge) processTemperatures(acc telegraf.Accumulator) error {
getTemperaturesResponse, err := b.resolvedClient.GetTemperatures()
func (b *bridge) processTemperatures(ctx context.Context, acc telegraf.Accumulator) error {
getTemperaturesResponse, err := b.resolvedClient.GetTemperatures(ctx)
if err != nil {
return fmt.Errorf("failed to access bridge temperatures on %s: %w", b, err)
}
if getTemperaturesResponse.HTTPResponse.StatusCode != http.StatusOK {
return fmt.Errorf("failed to fetch bridge temperatures from %s: %s", b, getTemperaturesResponse.HTTPResponse.Status)
}
responseData := getTemperaturesResponse.JSON200.Data
if responseData != nil {
for _, temperature := range *responseData {
temperatureName := b.resolveDeviceName(*temperature.Id)
tags := make(map[string]string)
tags["bridge_id"] = b.resolvedClient.Bridge().BridgeId
tags["room"] = b.resolveResourceRoom(*temperature.Id, temperatureName)
tags["device"] = temperatureName
tags["enabled"] = strconv.FormatBool(*temperature.Enabled)
fields := make(map[string]interface{})
fields["temperature"] = *temperature.Temperature.TemperatureReport.Temperature
acc.AddGauge("huebridge_temperature", fields, tags)
}
for _, temperature := range responseData {
temperatureName := b.resolveDeviceName(temperature.Id)
tags := make(map[string]string)
tags["bridge_id"] = b.resolvedClient.Bridge().BridgeId
tags["room"] = b.resolveResourceRoom(temperature.Id, temperatureName)
tags["device"] = temperatureName
tags["enabled"] = strconv.FormatBool(temperature.Enabled)
fields := make(map[string]interface{})
fields["temperature"] = *temperature.Temperature.TemperatureReport.Temperature
acc.AddGauge("huebridge_temperature", fields, tags)
}
return nil
}

func (b *bridge) processLightLevels(acc telegraf.Accumulator) error {
getLightLevelsResponse, err := b.resolvedClient.GetLightLevels()
func (b *bridge) processLightLevels(ctx context.Context, acc telegraf.Accumulator) error {
getLightLevelsResponse, err := b.resolvedClient.GetLightLevels(ctx)
if err != nil {
return fmt.Errorf("failed to access bridge lights levels on %s: %w", b, err)
}
if getLightLevelsResponse.HTTPResponse.StatusCode != http.StatusOK {
return fmt.Errorf("failed to fetch bridge light levels from %s: %s", b, getLightLevelsResponse.HTTPResponse.Status)
}
responseData := getLightLevelsResponse.JSON200.Data
if responseData != nil {
for _, lightLevel := range *responseData {
lightLevelName := b.resolveDeviceName(*lightLevel.Id)
tags := make(map[string]string)
tags["bridge_id"] = b.resolvedClient.Bridge().BridgeId
tags["room"] = b.resolveResourceRoom(*lightLevel.Id, lightLevelName)
tags["device"] = lightLevelName
tags["enabled"] = strconv.FormatBool(*lightLevel.Enabled)
fields := make(map[string]interface{})
fields["light_level"] = *lightLevel.Light.LightLevelReport.LightLevel
fields["light_level_lux"] = math.Pow(10.0, (float64(*lightLevel.Light.LightLevelReport.LightLevel)-1.0)/10000.0)
acc.AddGauge("huebridge_light_level", fields, tags)
}
for _, lightLevel := range responseData {
lightLevelName := b.resolveDeviceName(lightLevel.Id)
tags := make(map[string]string)
tags["bridge_id"] = b.resolvedClient.Bridge().BridgeId
tags["room"] = b.resolveResourceRoom(lightLevel.Id, lightLevelName)
tags["device"] = lightLevelName
tags["enabled"] = strconv.FormatBool(lightLevel.Enabled)
fields := make(map[string]interface{})
fields["light_level"] = *lightLevel.Light.LightLevelReport.LightLevel
fields["light_level_lux"] = math.Pow(10.0, (float64(*lightLevel.Light.LightLevelReport.LightLevel)-1.0)/10000.0)
acc.AddGauge("huebridge_light_level", fields, tags)
}
return nil
}

func (b *bridge) processMotionSensors(acc telegraf.Accumulator) error {
getMotionSensorsResponse, err := b.resolvedClient.GetMotionSensors()
func (b *bridge) processMotionSensors(ctx context.Context, acc telegraf.Accumulator) error {
getMotionSensorsResponse, err := b.resolvedClient.GetMotionSensors(ctx)
if err != nil {
return fmt.Errorf("failed to access bridge motion sensors on %s: %w", b, err)
}
if getMotionSensorsResponse.HTTPResponse.StatusCode != http.StatusOK {
return fmt.Errorf("failed to fetch bridge motion sensors from %s: %s", b, getMotionSensorsResponse.HTTPResponse.Status)
}
responseData := getMotionSensorsResponse.JSON200.Data
if responseData != nil {
for _, motionSensor := range *responseData {
motionSensorName := b.resolveDeviceName(*motionSensor.Id)
tags := make(map[string]string)
tags["bridge_id"] = b.resolvedClient.Bridge().BridgeId
tags["room"] = b.resolveResourceRoom(*motionSensor.Id, motionSensorName)
tags["device"] = motionSensorName
tags["enabled"] = strconv.FormatBool(*motionSensor.Enabled)
fields := make(map[string]interface{})
if *motionSensor.Motion.MotionReport.Motion {
fields["motion"] = 1
} else {
fields["motion"] = 0
}
acc.AddGauge("huebridge_motion_sensor", fields, tags)
for _, motionSensor := range responseData {
motionSensorName := b.resolveDeviceName(motionSensor.Id)
tags := make(map[string]string)
tags["bridge_id"] = b.resolvedClient.Bridge().BridgeId
tags["room"] = b.resolveResourceRoom(motionSensor.Id, motionSensorName)
tags["device"] = motionSensorName
tags["enabled"] = strconv.FormatBool(motionSensor.Enabled)
fields := make(map[string]interface{})
if *motionSensor.Motion.MotionReport.Motion {
fields["motion"] = 1
} else {
fields["motion"] = 0
}
acc.AddGauge("huebridge_motion_sensor", fields, tags)
}
return nil
}

func (b *bridge) processDevicePowers(acc telegraf.Accumulator) error {
getDevicePowersResponse, err := b.resolvedClient.GetDevicePowers()
func (b *bridge) processDevicePowers(ctx context.Context, acc telegraf.Accumulator) error {
getDevicePowersResponse, err := b.resolvedClient.GetDevicePowers(ctx)
if err != nil {
return fmt.Errorf("failed to access bridge device powers on %s: %w", b, err)
}
if getDevicePowersResponse.HTTPResponse.StatusCode != http.StatusOK {
return fmt.Errorf("failed to fetch bridge device powers from %s: %s", b, getDevicePowersResponse.HTTPResponse.Status)
}
responseData := getDevicePowersResponse.JSON200.Data
if responseData != nil {
for _, devicePower := range *responseData {
if devicePower.PowerState.BatteryLevel == nil && devicePower.PowerState.BatteryState == nil {
continue
}
devicePowerName := b.resolveDeviceName(*devicePower.Id)
tags := make(map[string]string)
tags["bridge_id"] = b.resolvedClient.Bridge().BridgeId
tags["room"] = b.resolveResourceRoom(*devicePower.Id, devicePowerName)
tags["device"] = devicePowerName
fields := make(map[string]interface{})
fields["battery_level"] = *devicePower.PowerState.BatteryLevel
fields["battery_state"] = *devicePower.PowerState.BatteryState
acc.AddGauge("huebridge_device_power", fields, tags)
for _, devicePower := range responseData {
if devicePower.PowerState.BatteryLevel == nil && devicePower.PowerState.BatteryState == nil {
continue
}
devicePowerName := b.resolveDeviceName(devicePower.Id)
tags := make(map[string]string)
tags["bridge_id"] = b.resolvedClient.Bridge().BridgeId
tags["room"] = b.resolveResourceRoom(devicePower.Id, devicePowerName)
tags["device"] = devicePowerName
fields := make(map[string]interface{})
fields["battery_level"] = *devicePower.PowerState.BatteryLevel
fields["battery_state"] = *devicePower.PowerState.BatteryState
acc.AddGauge("huebridge_device_power", fields, tags)
}
return nil
}
Expand Down Expand Up @@ -295,20 +286,20 @@ func (b *bridge) resolveRemoteBridge(locator *hue.RemoteBridgeLocator) error {
return nil
}

func (b *bridge) fetchMetadata() error {
err := b.fetchResourceTree()
func (b *bridge) fetchMetadata(ctx context.Context) error {
err := b.fetchResourceTree(ctx)
if err != nil {
return err
}
err = b.fetchDeviceNames()
err = b.fetchDeviceNames(ctx)
if err != nil {
return err
}
return b.fetchRoomAssignments()
return b.fetchRoomAssignments(ctx)
}

func (b *bridge) fetchResourceTree() error {
getResourcesResponse, err := b.resolvedClient.GetResources()
func (b *bridge) fetchResourceTree(ctx context.Context) error {
getResourcesResponse, err := b.resolvedClient.GetResources(ctx)
if err != nil {
return fmt.Errorf("failed to access bridge resources on %s: %w", b, err)
}
Expand All @@ -322,15 +313,13 @@ func (b *bridge) fetchResourceTree() error {
}
b.resourceTree = make(map[string]string, len(*responseData))
for _, resource := range *responseData {
if resource.Owner != nil {
b.resourceTree[*resource.Id] = *resource.Owner.Rid
}
b.resourceTree[resource.Id] = resource.Owner.Rid
}
return nil
}

func (b *bridge) fetchDeviceNames() error {
getDevicesResponse, err := b.resolvedClient.GetDevices()
func (b *bridge) fetchDeviceNames(ctx context.Context) error {
getDevicesResponse, err := b.resolvedClient.GetDevices(ctx)
if err != nil {
return fmt.Errorf("failed to access bridge devices on %s: %w", b, err)
}
Expand All @@ -342,15 +331,15 @@ func (b *bridge) fetchDeviceNames() error {
b.deviceNames = make(map[string]string)
return nil
}
b.deviceNames = make(map[string]string, len(*responseData))
for _, device := range *responseData {
b.deviceNames[*device.Id] = *device.Metadata.Name
b.deviceNames = make(map[string]string, len(responseData))
for _, device := range responseData {
b.deviceNames[device.Id] = device.Metadata.Name
}
return nil
}

func (b *bridge) fetchRoomAssignments() error {
getRoomsResponse, err := b.resolvedClient.GetRooms()
func (b *bridge) fetchRoomAssignments(ctx context.Context) error {
getRoomsResponse, err := b.resolvedClient.GetRooms(ctx)
if err != nil {
return fmt.Errorf("failed to access bridge rooms on %s: %w", b, err)
}
Expand All @@ -362,10 +351,10 @@ func (b *bridge) fetchRoomAssignments() error {
b.roomAssignments = maps.Clone(b.configRoomAssignments)
return nil
}
b.roomAssignments = make(map[string]string, len(*responseData))
for _, roomGet := range *responseData {
for _, children := range *roomGet.Children {
b.roomAssignments[*children.Rid] = *roomGet.Metadata.Name
b.roomAssignments = make(map[string]string, len(responseData))
for _, roomGet := range responseData {
for _, children := range roomGet.Children {
b.roomAssignments[children.Rid] = roomGet.Metadata.Name
}
}
maps.Copy(b.roomAssignments, b.configRoomAssignments)
Expand Down
Loading
Loading