diff --git a/internal/linux/apps/apps.go b/internal/linux/apps/apps.go index 636e42730..6824c1739 100644 --- a/internal/linux/apps/apps.go +++ b/internal/linux/apps/apps.go @@ -33,19 +33,21 @@ type worker struct { } //nolint:exhaustruct -func (w *worker) Setup(_ context.Context) (*dbusx.Watch, error) { - return &dbusx.Watch{ - Bus: dbusx.SessionBus, - Path: appStateDBusPath, - Interface: appStateDBusInterface, - Names: []string{"RunningApplicationsChanged"}, - }, - nil -} - -func (w *worker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan sensor.Details { +func (w *worker) Events(ctx context.Context) (chan sensor.Details, error) { sensorCh := make(chan sensor.Details) + triggerCh, err := dbusx.WatchBus(ctx, &dbusx.Watch{ + Bus: dbusx.SessionBus, + Path: appStateDBusPath, + Interface: appStateDBusInterface, + Names: []string{"RunningApplicationsChanged"}, + }) + if err != nil { + close(sensorCh) + + return sensorCh, fmt.Errorf("could not watch D-Bus for app state events: %w", err) + } + sendSensors := func(ctx context.Context, sensorCh chan sensor.Details) { appSensors, err := w.Sensors(ctx) if err != nil { @@ -80,7 +82,7 @@ func (w *worker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan s sendSensors(ctx, sensorCh) }() - return sensorCh + return sensorCh, nil } func (w *worker) Sensors(ctx context.Context) ([]sensor.Details, error) { diff --git a/internal/linux/battery/battery.go b/internal/linux/battery/battery.go index 3010706d8..e2c2b13a5 100644 --- a/internal/linux/battery/battery.go +++ b/internal/linux/battery/battery.go @@ -494,7 +494,7 @@ func (w *worker) Sensors(_ context.Context) ([]sensor.Details, error) { } //nolint:prealloc -func (w *worker) Events(ctx context.Context) chan sensor.Details { +func (w *worker) Events(ctx context.Context) (chan sensor.Details, error) { batteryTracker := newBatteryTracker() var sensorCh []<-chan sensor.Details @@ -512,7 +512,7 @@ func (w *worker) Events(ctx context.Context) chan sensor.Details { // Monitor for battery added/removed signals. sensorCh = append(sensorCh, monitorBatteryChanges(ctx, batteryTracker)) - return sensor.MergeSensorCh(ctx, sensorCh...) + return sensor.MergeSensorCh(ctx, sensorCh...), nil } func NewBatteryWorker() (*linux.SensorWorker, error) { diff --git a/internal/linux/desktop/desktop.go b/internal/linux/desktop/desktop.go index 5ec4475d3..9d9a20a55 100644 --- a/internal/linux/desktop/desktop.go +++ b/internal/linux/desktop/desktop.go @@ -42,19 +42,21 @@ type desktopSettingSensor struct { type worker struct{} //nolint:exhaustruct -func (w *worker) Setup(_ context.Context) (*dbusx.Watch, error) { - return &dbusx.Watch{ - Bus: dbusx.SessionBus, - Names: []string{settingsChangedSignal}, - Interface: settingsPortalInterface, - Path: desktopPortalPath, - }, - nil -} - -func (w *worker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan sensor.Details { +func (w *worker) Events(ctx context.Context) (chan sensor.Details, error) { sensorCh := make(chan sensor.Details) + triggerCh, err := dbusx.WatchBus(ctx, &dbusx.Watch{ + Bus: dbusx.SessionBus, + Names: []string{settingsChangedSignal}, + Interface: settingsPortalInterface, + Path: desktopPortalPath, + }) + if err != nil { + close(sensorCh) + + return sensorCh, fmt.Errorf("could not watch D-Bus for desktop settings updates: %w", err) + } + go func() { defer close(sensorCh) @@ -94,7 +96,7 @@ func (w *worker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan s } }() - return sensorCh + return sensorCh, nil } //nolint:mnd diff --git a/internal/linux/location/location.go b/internal/linux/location/location.go index d3736cc69..a60a7c7a4 100644 --- a/internal/linux/location/location.go +++ b/internal/linux/location/location.go @@ -47,7 +47,7 @@ type worker struct { } //nolint:exhaustruct -func (w *worker) Setup(ctx context.Context) (*dbusx.Watch, error) { +func (w *worker) setup(ctx context.Context) (*dbusx.Watch, error) { var err error // Check if we can create a client, bail if we can't. @@ -88,9 +88,23 @@ func (w *worker) Setup(ctx context.Context) (*dbusx.Watch, error) { nil } -func (w *worker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan sensor.Details { +func (w *worker) Events(ctx context.Context) (chan sensor.Details, error) { sensorCh := make(chan sensor.Details) + watch, err := w.setup(ctx) + if err != nil { + close(sensorCh) + + return sensorCh, fmt.Errorf("could not setup D-Bus watch for location updates: %w", err) + } + + triggerCh, err := dbusx.WatchBus(ctx, watch) + if err != nil { + close(sensorCh) + + return sensorCh, fmt.Errorf("could not watch D-Bus for location updates: %w", err) + } + go func() { defer close(sensorCh) @@ -115,7 +129,7 @@ func (w *worker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan s } }() - return sensorCh + return sensorCh, nil } func (w *worker) Sensors(_ context.Context) ([]sensor.Details, error) { @@ -126,7 +140,7 @@ func (w *worker) Sensors(_ context.Context) ([]sensor.Details, error) { func NewLocationWorker() (*linux.SensorWorker, error) { // Don't run this worker if we are not running on a laptop. if linux.Chassis() != "laptop" { - return nil, linux.ErrUnsupportedHardware + return nil, fmt.Errorf("will not start location sensor: %w", linux.ErrUnsupportedHardware) } return &linux.SensorWorker{ diff --git a/internal/linux/net/networkConnection.go b/internal/linux/net/networkConnection.go index a40452bbd..fe1aa6b30 100644 --- a/internal/linux/net/networkConnection.go +++ b/internal/linux/net/networkConnection.go @@ -8,6 +8,7 @@ package net import ( "context" + "fmt" "slices" "sync" @@ -432,7 +433,7 @@ func (w *connectionsWorker) Sensors(_ context.Context) ([]sensor.Details, error) } //nolint:exhaustruct,mnd -func (w *connectionsWorker) Events(ctx context.Context) chan sensor.Details { +func (w *connectionsWorker) Events(ctx context.Context) (chan sensor.Details, error) { sensorCh := make(chan sensor.Details) w.list = getActiveConnections(ctx) handleConn := func(path dbus.ObjectPath) { @@ -453,11 +454,9 @@ func (w *connectionsWorker) Events(ctx context.Context) chan sensor.Details { Interface: dbusNMActiveConnIntr, }) if err != nil { - log.Debug().Err(err). - Msg("Failed to create network connections D-Bus watch.") close(sensorCh) - return sensorCh + return sensorCh, fmt.Errorf("failed to create network connections D-Bus watch: %w", err) } go func() { @@ -494,7 +493,7 @@ func (w *connectionsWorker) Events(ctx context.Context) chan sensor.Details { handleConn(path) } - return sensorCh + return sensorCh, nil } //nolint:exhaustruct diff --git a/internal/linux/power/laptop.go b/internal/linux/power/laptop.go index 5953964d0..8b22b7d7d 100644 --- a/internal/linux/power/laptop.go +++ b/internal/linux/power/laptop.go @@ -86,31 +86,31 @@ func newLaptopEvent(prop string, state bool) *laptopSensor { return sensorEvent } -type laptopWorker struct { - sessionPath string -} +type laptopWorker struct{} + +//nolint:cyclop,exhaustruct +func (w *laptopWorker) Events(ctx context.Context) (chan sensor.Details, error) { + sensorCh := make(chan sensor.Details) -//nolint:exhaustruct -func (w *laptopWorker) Setup(ctx context.Context) (*dbusx.Watch, error) { // If we can't get a session path, we can't run. sessionPath, err := dbusx.GetSessionPath(ctx) if err != nil { - return nil, fmt.Errorf("could not create laptop worker: %w", err) - } + close(sensorCh) - w.sessionPath = sessionPath + return sensorCh, fmt.Errorf("could not create laptop worker: %w", err) + } - return &dbusx.Watch{ - Bus: dbusx.SystemBus, - Names: []string{dbusx.PropChangedSignal}, - Interface: managerInterface, - Path: w.sessionPath, - }, - nil -} + triggerCh, err := dbusx.WatchBus(ctx, &dbusx.Watch{ + Bus: dbusx.SystemBus, + Names: []string{dbusx.PropChangedSignal}, + Interface: managerInterface, + Path: sessionPath, + }) + if err != nil { + close(sensorCh) -func (w *laptopWorker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan sensor.Details { - sensorCh := make(chan sensor.Details) + return sensorCh, fmt.Errorf("could not watch D-Bus for laptop updates: %w", err) + } go func() { defer close(sensorCh) @@ -150,7 +150,7 @@ func (w *laptopWorker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) } }() - return sensorCh + return sensorCh, nil } func (w *laptopWorker) Sensors(ctx context.Context) ([]sensor.Details, error) { @@ -170,11 +170,10 @@ func (w *laptopWorker) Sensors(ctx context.Context) ([]sensor.Details, error) { return sensors, nil } -//nolint:exhaustruct func NewLaptopWorker() (*linux.SensorWorker, error) { // Don't run this worker if we are not running on a laptop. if linux.Chassis() != "laptop" { - return nil, linux.ErrUnsupportedHardware + return nil, fmt.Errorf("will not create laptop sensors: %w", linux.ErrUnsupportedHardware) } return &linux.SensorWorker{ diff --git a/internal/linux/power/powerProfile.go b/internal/linux/power/powerProfile.go index 7ceb7ac13..10c8363c9 100644 --- a/internal/linux/power/powerProfile.go +++ b/internal/linux/power/powerProfile.go @@ -44,33 +44,34 @@ func newPowerSensor(sensorType linux.SensorTypeValue, sensorValue dbus.Variant) type profileWorker struct{} //nolint:exhaustruct -func (w *profileWorker) Setup(_ context.Context) (*dbusx.Watch, error) { - return &dbusx.Watch{ - Bus: dbusx.SystemBus, - Names: []string{dbusx.PropChangedSignal}, - Interface: dbusx.PropInterface, - Path: powerProfilesPath, - }, - nil -} - -func (w *profileWorker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan sensor.Details { +func (w *profileWorker) Events(ctx context.Context) (chan sensor.Details, error) { sensorCh := make(chan sensor.Details) // Check for power profile support, exit if not available. Otherwise, send // an initial update. sensors, err := w.Sensors(ctx) if err != nil { - log.Warn().Err(err).Msg("Cannot monitor power profile.") close(sensorCh) - return sensorCh + return sensorCh, fmt.Errorf("cannot retrieve power profile: %w", err) } go func() { sensorCh <- sensors[0] }() + triggerCh, err := dbusx.WatchBus(ctx, &dbusx.Watch{ + Bus: dbusx.SystemBus, + Names: []string{dbusx.PropChangedSignal}, + Interface: dbusx.PropInterface, + Path: powerProfilesPath, + }) + if err != nil { + close(sensorCh) + + return sensorCh, fmt.Errorf("could not watch D-Bus for power profile updates: %w", err) + } + // Watch for power profile changes. go func() { defer close(sensorCh) @@ -96,7 +97,7 @@ func (w *profileWorker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) } }() - return sensorCh + return sensorCh, nil } func (w *profileWorker) Sensors(ctx context.Context) ([]sensor.Details, error) { diff --git a/internal/linux/power/powerState.go b/internal/linux/power/powerState.go index b6c12f45c..a476752bc 100644 --- a/internal/linux/power/powerState.go +++ b/internal/linux/power/powerState.go @@ -8,6 +8,7 @@ package power import ( "context" + "fmt" "github.com/rs/zerolog/log" @@ -81,19 +82,21 @@ func newPowerState(signalName powerSignal, signalValue any) *powerStateSensor { type stateWorker struct{} //nolint:exhaustruct -func (w *stateWorker) Setup(_ context.Context) (*dbusx.Watch, error) { - return &dbusx.Watch{ - Bus: dbusx.SystemBus, - Names: []string{sleepSignal, shutdownSignal}, - Interface: managerInterface, - Path: loginBasePath, - }, - nil -} - -func (w *stateWorker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan sensor.Details { +func (w *stateWorker) Events(ctx context.Context) (chan sensor.Details, error) { sensorCh := make(chan sensor.Details) + triggerCh, err := dbusx.WatchBus(ctx, &dbusx.Watch{ + Bus: dbusx.SystemBus, + Names: []string{sleepSignal, shutdownSignal}, + Interface: managerInterface, + Path: loginBasePath, + }) + if err != nil { + close(sensorCh) + + return sensorCh, fmt.Errorf("could not watch D-Bus for power state updates: %w", err) + } + // Watch for state changes. go func() { defer close(sensorCh) @@ -133,7 +136,7 @@ func (w *stateWorker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) c } }() - return sensorCh + return sensorCh, nil } // Sensors returns the current sensors states. Assuming that if this is called, diff --git a/internal/linux/power/screenLock.go b/internal/linux/power/screenLock.go index 4e07774c0..4b965b630 100644 --- a/internal/linux/power/screenLock.go +++ b/internal/linux/power/screenLock.go @@ -46,30 +46,29 @@ func newScreenlockEvent(value bool) *screenlockSensor { } } -type screenLockWorker struct { - sessionPath string -} +type screenLockWorker struct{} + +//nolint:cyclop,exhaustruct +func (w *screenLockWorker) Events(ctx context.Context) (chan sensor.Details, error) { + sensorCh := make(chan sensor.Details) -//nolint:exhaustruct -func (w *screenLockWorker) Setup(ctx context.Context) (*dbusx.Watch, error) { sessionPath, err := dbusx.GetSessionPath(ctx) if err != nil { return nil, fmt.Errorf("could not create screen lock worker: %w", err) } - w.sessionPath = sessionPath + triggerCh, err := dbusx.WatchBus(ctx, &dbusx.Watch{ + Bus: dbusx.SystemBus, + Names: []string{sessionLockSignal, sessionUnlockSignal, sessionLockedProp}, + Interface: sessionInterface, + Path: sessionPath, + }) + if err != nil { + close(sensorCh) - return &dbusx.Watch{ - Bus: dbusx.SystemBus, - Names: []string{sessionLockSignal, sessionUnlockSignal, sessionLockedProp}, - Interface: sessionInterface, - Path: w.sessionPath, - }, - nil -} + return sensorCh, fmt.Errorf("could not watch D-Bus for screen lock updates: %w", err) + } -func (w *screenLockWorker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan sensor.Details { - sensorCh := make(chan sensor.Details) go func() { defer close(sensorCh) @@ -101,7 +100,7 @@ func (w *screenLockWorker) Watch(ctx context.Context, triggerCh chan dbusx.Trigg } }() - return sensorCh + return sensorCh, nil } // ?: retrieve the current screen lock state when called. @@ -109,7 +108,6 @@ func (w *screenLockWorker) Sensors(_ context.Context) ([]sensor.Details, error) return nil, linux.ErrUnimplemented } -//nolint:exhaustruct func NewScreenLockWorker() (*linux.SensorWorker, error) { return &linux.SensorWorker{ WorkerName: "Screen Lock Sensor", diff --git a/internal/linux/user/users.go b/internal/linux/user/users.go index 6a0f62018..d83907bb0 100644 --- a/internal/linux/user/users.go +++ b/internal/linux/user/users.go @@ -80,19 +80,21 @@ type worker struct { } //nolint:exhaustruct -func (w *worker) Setup(_ context.Context) (*dbusx.Watch, error) { - return &dbusx.Watch{ - Bus: dbusx.SystemBus, - Names: []string{sessionAddedSignal, sessionRemovedSignal}, - Interface: managerInterface, - Path: loginBasePath, - }, - nil -} - -func (w *worker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan sensor.Details { +func (w *worker) Events(ctx context.Context) (chan sensor.Details, error) { sensorCh := make(chan sensor.Details) + triggerCh, err := dbusx.WatchBus(ctx, &dbusx.Watch{ + Bus: dbusx.SystemBus, + Names: []string{sessionAddedSignal, sessionRemovedSignal}, + Interface: managerInterface, + Path: loginBasePath, + }) + if err != nil { + close(sensorCh) + + return sensorCh, fmt.Errorf("could not watch D-Bus for user updates: %w", err) + } + sendUpdate := func() { err := w.sensor.updateUsers(ctx) if err != nil { @@ -125,7 +127,7 @@ func (w *worker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan s // Send an initial sensor update. go sendUpdate() - return sensorCh + return sensorCh, nil } func (w *worker) Sensors(ctx context.Context) ([]sensor.Details, error) { diff --git a/internal/linux/worker.go b/internal/linux/worker.go index fc3035df9..d6229d0af 100644 --- a/internal/linux/worker.go +++ b/internal/linux/worker.go @@ -15,7 +15,6 @@ import ( "github.com/joshuar/go-hass-agent/internal/device/helpers" "github.com/joshuar/go-hass-agent/internal/hass/sensor" - "github.com/joshuar/go-hass-agent/pkg/linux/dbusx" ) var ErrUnknownWorker = errors.New("unknown sensor worker type") @@ -27,17 +26,10 @@ type pollingType interface { Sensors(ctx context.Context, delta time.Duration) ([]sensor.Details, error) } -// dbusType interface represents sensors that are generated on D-Bus events. -type dbusType interface { - Setup(ctx context.Context) (*dbusx.Watch, error) - Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan sensor.Details - Sensors(ctx context.Context) ([]sensor.Details, error) -} - -// eventType interface represents sensors that are generated by some means other -// than D-Bus signals. +// eventType interface represents sensors that are generated on some event +// trigger, such as D-Bus messages. type eventType interface { - Events(ctx context.Context) chan sensor.Details + Events(ctx context.Context) (chan sensor.Details, error) Sensors(ctx context.Context) ([]sensor.Details, error) } @@ -80,13 +72,6 @@ func (w *SensorWorker) Sensors(ctx context.Context) ([]sensor.Details, error) { return nil, fmt.Errorf("failed to get current state of polling sensors: %w", err) } - return sensors, nil - case dbusType: - sensors, err := worker.Sensors(ctx) - if err != nil { - return nil, fmt.Errorf("failed to get current state of polling sensors: %w", err) - } - return sensors, nil case eventType: sensors, err := worker.Sensors(ctx) @@ -138,41 +123,22 @@ func (w *SensorWorker) Updates(ctx context.Context) (<-chan sensor.Details, erro log.Trace().Str("worker", w.Name()).Msg("Polling for sensor updates...") helpers.PollSensors(ctx, updater, worker.Interval(), worker.Jitter()) }() - case dbusType: - // dbusType: run the worker Setup function, bail if it fails. Else, run - // the worker WatchBus function, which spits out sensors based on the - // D-Bus watch. - watch, err := worker.Setup(ctx) - if err != nil { - close(outCh) - - return outCh, fmt.Errorf("could not set up watch for worker: %w", err) - } - - log.Trace().Str("worker", w.Name()).Msg("Watching D-Bus for sensor updates...") - - eventCh, err := dbusx.WatchBus(ctx, watch) - if err != nil { - close(outCh) - - return outCh, fmt.Errorf("could not watch D-Bus for worker: %w", err) - } - - go func() { - defer close(outCh) - - for s := range worker.Watch(ctx, eventCh) { - outCh <- s - } - }() case eventType: // eventType: read sensors from the worker Events function and pass // these on. go func() { defer close(outCh) + + eventCh, err := worker.Events(ctx) + if err != nil { + log.Debug().Err(err).Msg("Could not start event worker.") + + return + } + log.Trace().Str("worker", w.Name()).Msg("Listening for sensor update events...") - for s := range worker.Events(ctx) { + for s := range eventCh { outCh <- s } }() @@ -198,7 +164,7 @@ func (w *SensorWorker) Updates(ctx context.Context) (<-chan sensor.Details, erro default: // default: we should not get here, so if we do, return an error // indicating we don't know what type of worker this is. - return nil, ErrUnknownWorker + return nil, fmt.Errorf("could not track updates for %s: %w", w.Name(), ErrUnknownWorker) } return outCh, nil