diff --git a/internal/agent/agent.go b/internal/agent/agent.go index f7c8b8728..6a481dc9b 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -23,6 +23,8 @@ import ( "github.com/joshuar/go-hass-agent/internal/hass/sensor" ) +var ErrCtxFailed = errors.New("unable to create a context") + // Agent holds the data and structure representing an instance of the agent. // This includes the data structure for the UI elements and tray and some // strings such as app name and version. @@ -39,15 +41,16 @@ type Options struct { Headless, ForceRegister, IgnoreURLs bool } +//nolint:exhaustruct func New(o *Options) *Agent { - a := &Agent{ + agent := &Agent{ done: make(chan struct{}), Options: o, } - if !a.Options.Headless { - a.ui = fyneui.NewFyneUI(a.Options.ID) + if !agent.Options.Headless { + agent.ui = fyneui.NewFyneUI(agent.Options.ID) } - return a + return agent } // Run is the "main loop" of the agent. It sets up the agent, loads the config @@ -58,7 +61,9 @@ func (agent *Agent) Run(trk SensorTracker, reg sensor.Registry) { // Pre-flight: check if agent is registered. If not, run registration flow. var regWait sync.WaitGroup + regWait.Add(1) + go func() { defer regWait.Done() if err := agent.checkRegistration(trk); err != nil { @@ -67,6 +72,7 @@ func (agent *Agent) Run(trk SensorTracker, reg sensor.Registry) { }() wg.Add(1) + go func() { defer wg.Done() regWait.Wait() @@ -86,12 +92,14 @@ func (agent *Agent) Run(trk SensorTracker, reg sensor.Registry) { // Start worker funcs for sensors. wg.Add(1) + go func() { defer wg.Done() runWorkers(runnerCtx, trk, reg) }() // Start any scripts. wg.Add(1) + go func() { defer wg.Done() scriptPath := filepath.Join(xdg.ConfigHome, agent.AppID(), "scripts") @@ -99,6 +107,7 @@ func (agent *Agent) Run(trk SensorTracker, reg sensor.Registry) { }() // Start the mqtt client wg.Add(1) + go func() { defer wg.Done() runMQTTWorker(runnerCtx) @@ -106,6 +115,7 @@ func (agent *Agent) Run(trk SensorTracker, reg sensor.Registry) { // Listen for notifications from Home Assistant. if !agent.IsHeadless() { wg.Add(1) + go func() { defer wg.Done() agent.runNotificationsWorker(runnerCtx) @@ -126,6 +136,7 @@ func (agent *Agent) Register(trk SensorTracker) { var wg sync.WaitGroup wg.Add(1) + go func() { defer wg.Done() if err := agent.checkRegistration(trk); err != nil { @@ -144,6 +155,7 @@ func (agent *Agent) Register(trk SensorTracker) { func (agent *Agent) handleSignals() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) + go func() { defer close(agent.done) <-c @@ -173,7 +185,7 @@ func (agent *Agent) Stop() { func (agent *Agent) Reset() error { ctx, _ := hass.NewContext() if ctx == nil { - return errors.New("unable to create a context") + return ErrCtxFailed } runnerCtx := setupDeviceContext(ctx) resetMQTTWorker(runnerCtx) diff --git a/internal/agent/device_linux.go b/internal/agent/device_linux.go index 154974266..8c10be868 100644 --- a/internal/agent/device_linux.go +++ b/internal/agent/device_linux.go @@ -59,13 +59,16 @@ func newDevice(_ context.Context) *linux.Device { // sensorWorkers initialises the list of workers for sensors and returns those // that are supported on this device. func sensorWorkers() []Worker { - var activeWorkers []Worker + activeWorkers := make([]Worker, 0, len(workers)) + for _, w := range workers { worker, err := w() if err != nil { log.Warn().Err(err).Msg("Could not activate a worker.") + continue } + activeWorkers = append(activeWorkers, worker) } return activeWorkers diff --git a/internal/agent/runners.go b/internal/agent/runners.go index c4c82a36a..a43d5b883 100644 --- a/internal/agent/runners.go +++ b/internal/agent/runners.go @@ -48,6 +48,7 @@ func runWorkers(ctx context.Context, trk SensorTracker, reg sensor.Registry) { cancelFuncs := make([]context.CancelFunc, 0, len(workers)) log.Debug().Msg("Starting worker funcs.") + for worker := range len(workers) { workerCtx, cancelFunc := context.WithCancel(ctx) cancelFuncs = append(cancelFuncs, cancelFunc) @@ -67,6 +68,7 @@ func runWorkers(ctx context.Context, trk SensorTracker, reg sensor.Registry) { sensorUpdates := sensor.MergeSensorCh(ctx, outCh...) go func() { log.Debug().Msg("Listening for sensor updates.") + for update := range sensorUpdates { go func(update sensor.Details) { if err := trk.UpdateSensor(ctx, reg, update); err != nil { @@ -85,6 +87,7 @@ func runWorkers(ctx context.Context, trk SensorTracker, reg sensor.Registry) { go func() { <-ctx.Done() + for _, c := range cancelFuncs { c() } @@ -129,6 +132,7 @@ func runScripts(ctx context.Context, path string, trk SensorTracker, reg sensor. } log.Debug().Msg("Starting cron scheduler for script sensors.") scheduler.Start() + go func() { for scriptUpdates := range sensor.MergeSensorCh(ctx, outCh...) { go func(update sensor.Details) { @@ -162,6 +166,7 @@ func (agent *Agent) runNotificationsWorker(ctx context.Context) { var wg sync.WaitGroup wg.Add(1) + go func() { defer wg.Done() diff --git a/internal/agent/ui/strings.go b/internal/agent/ui/strings.go index 1e5d5313b..7b3e7e548 100644 --- a/internal/agent/ui/strings.go +++ b/internal/agent/ui/strings.go @@ -3,6 +3,7 @@ // This software is released under the MIT License. // https://opensource.org/licenses/MIT +//nolint:lll package ui const (