joshuar-go-hass-agent/internal/agent/device.go

134 lines
2.9 KiB
Go

// Copyright (c) 2024 Joshua Rich <joshua.rich@gmail.com>
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
package agent
import (
"context"
"errors"
"fmt"
"log/slog"
"github.com/joshuar/go-hass-agent/internal/device"
"github.com/joshuar/go-hass-agent/internal/hass/sensor"
"github.com/joshuar/go-hass-agent/internal/logging"
)
type sensorWorker struct {
object Worker
started bool
}
type deviceController struct {
sensorWorkers map[string]*sensorWorker
logger *slog.Logger
}
func (w *deviceController) ActiveWorkers() []string {
activeWorkers := make([]string, 0, len(w.sensorWorkers))
for id, worker := range w.sensorWorkers {
if worker.started {
activeWorkers = append(activeWorkers, id)
}
}
return activeWorkers
}
func (w *deviceController) InactiveWorkers() []string {
inactiveWorkers := make([]string, 0, len(w.sensorWorkers))
for id, worker := range w.sensorWorkers {
if !worker.started {
inactiveWorkers = append(inactiveWorkers, id)
}
}
return inactiveWorkers
}
func (w *deviceController) Start(ctx context.Context, name string) (<-chan sensor.Details, error) {
worker, exists := w.sensorWorkers[name]
if !exists {
return nil, ErrUnknownWorker
}
if worker.started {
return nil, ErrWorkerAlreadyStarted
}
workerCh, err := w.sensorWorkers[name].object.Updates(ctx)
if err != nil {
return nil, fmt.Errorf("could not start worker: %w", err)
}
w.sensorWorkers[name].started = true
return workerCh, nil
}
func (w *deviceController) Stop(name string) error {
// Check if the given worker ID exists.
worker, exists := w.sensorWorkers[name]
if !exists {
return ErrUnknownWorker
}
// Stop the worker. Report any errors.
if err := worker.object.Stop(); err != nil {
return fmt.Errorf("error stopping worker: %w", err)
}
return nil
}
func (w *deviceController) StartAll(ctx context.Context) (<-chan sensor.Details, error) {
outCh := make([]<-chan sensor.Details, 0, len(allworkers))
var errs error
for id := range w.sensorWorkers {
workerCh, err := w.Start(ctx, id)
if err != nil {
errs = errors.Join(errs, err)
continue
}
outCh = append(outCh, workerCh)
}
return sensor.MergeSensorCh(ctx, outCh...), errs
}
func (w *deviceController) StopAll() error {
var errs error
for id := range w.sensorWorkers {
if err := w.Stop(id); err != nil {
errs = errors.Join(errs, err)
}
}
return errs
}
func newDeviceController(ctx context.Context) SensorController {
var worker Worker
controller := &deviceController{
sensorWorkers: make(map[string]*sensorWorker),
logger: logging.FromContext(ctx).With(slog.Group("device")),
}
// Set up sensor workers.
worker = device.NewVersionWorker()
controller.sensorWorkers[worker.ID()] = &sensorWorker{object: worker, started: false}
worker = device.NewExternalIPUpdaterWorker(ctx)
controller.sensorWorkers[worker.ID()] = &sensorWorker{object: worker, started: false}
return controller
}