Skip to content

Commit

Permalink
refactor: ♻️ make use of more interfaces between packages
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuar committed Jan 15, 2025
1 parent ac3c867 commit 5c8534c
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 66 deletions.
12 changes: 6 additions & 6 deletions internal/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ type Agent struct {
}

// newAgent creates the Agent struct.
func newAgent(ctx context.Context) *Agent {
func newAgent(ctx context.Context, tracker fyneui.Tracker) *Agent {
agent := &Agent{
logger: logging.FromContext(ctx).WithGroup("agent"),
}

// If not running headless, set up the UI.
if !preferences.Headless() {
agent.ui = fyneui.NewFyneUI(ctx)
agent.ui = fyneui.NewFyneUI(ctx, tracker)
}

return agent
Expand All @@ -55,7 +55,7 @@ func newAgent(ctx context.Context) *Agent {
// (i.e., `go-hass-agent run`).
//
//nolint:funlen
func Run(ctx context.Context, dataCh chan any) error {
func Run(ctx context.Context, dataCh chan any, tracker fyneui.Tracker) error {
var (
wg sync.WaitGroup
regWait sync.WaitGroup
Expand All @@ -68,7 +68,7 @@ func Run(ctx context.Context, dataCh chan any) error {
ctx, cancelFunc := context.WithCancel(ctx)
defer cancelFunc()

agent := newAgent(ctx)
agent := newAgent(ctx, tracker)

regWait.Add(1)

Expand Down Expand Up @@ -165,7 +165,7 @@ func Run(ctx context.Context, dataCh chan any) error {
func Register(ctx context.Context) error {
var wg sync.WaitGroup

agent := newAgent(ctx)
agent := newAgent(ctx, nil)

regCtx, cancelReg := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
go func() {
Expand Down Expand Up @@ -197,7 +197,7 @@ func Register(ctx context.Context) error {
// Reset is invoked when Go Hass Agent is run with the `reset` command-line
// option (i.e., `go-hass-agent reset`).
func Reset(ctx context.Context) error {
agent := newAgent(ctx)
agent := newAgent(ctx, nil)
// If MQTT is enabled, reset any saved MQTT config.
if preferences.MQTTEnabled() {
if err := resetMQTTWorkers(ctx); err != nil {
Expand Down
47 changes: 34 additions & 13 deletions internal/agent/ui/fyneUI/fyneUI.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

agentvalidator "github.com/joshuar/go-hass-agent/internal/components/validation"
"github.com/joshuar/go-hass-agent/internal/hass/discovery"
"github.com/joshuar/go-hass-agent/internal/hass/sensor"

"github.com/joshuar/go-hass-agent/internal/agent/ui"
"github.com/joshuar/go-hass-agent/internal/components/logging"
Expand All @@ -49,21 +50,32 @@ var (
ErrInvalidHostPort = errors.New(ui.InvalidHostPortMsgString)
)

// Notification represents the methods for displaying a notification.
type Notification interface {
GetTitle() string
GetMessage() string
}

// Tracker represents the methods for the UI to show the current states of
// sensors.
type Tracker interface {
Get(id string) (*sensor.Entity, error)
SensorList() []string
}

// FyneUI contains the data and methods to manage the UI state.
type FyneUI struct {
app fyne.App
logger *slog.Logger
app fyne.App
logger *slog.Logger
tracker Tracker
}

// New FyneUI sets up the UI for the agent.
func NewFyneUI(ctx context.Context) *FyneUI {
func NewFyneUI(ctx context.Context, tracker Tracker) *FyneUI {
appUI := &FyneUI{
app: app.NewWithID(preferences.AppName),
logger: logging.FromContext(ctx).With(slog.String("subsystem", "fyne")),
app: app.NewWithID(preferences.AppName),
logger: logging.FromContext(ctx).With(slog.String("subsystem", "fyne")),
tracker: tracker,
}
appUI.app.SetIcon(&ui.TrayIcon{})

Expand Down Expand Up @@ -205,11 +217,16 @@ func (i *FyneUI) aboutWindow(ctx context.Context) fyne.Window {
widget.NewLabelWithStyle("Home Assistant "+hass.Version(ctx),
fyne.TextAlignCenter,
fyne.TextStyle{Bold: true}),
widget.NewLabelWithStyle("Tracking "+strconv.Itoa(len(hass.SensorList()))+" Entities",
fyne.TextAlignCenter,
fyne.TextStyle{Italic: true}),
)

if i.tracker == nil {
widgets = append(widgets,
widget.NewLabelWithStyle("Tracking "+strconv.Itoa(len(i.tracker.SensorList()))+" Entities",
fyne.TextAlignCenter,
fyne.TextStyle{Italic: true}),
)
}

linkWidgets := generateLinks()
widgets = append(widgets,
widget.NewLabel(""),
Expand Down Expand Up @@ -284,19 +301,23 @@ func (i *FyneUI) agentSettingsWindow(ctx context.Context) fyne.Window {
//
//nolint:gocognit
func (i *FyneUI) sensorsWindow() fyne.Window {
sensors := hass.SensorList()
if i.tracker == nil {
i.logger.Error("Cannot show sensors, no sensor tracker loaded.")
}

sensors := i.tracker.SensorList()
if sensors == nil {
return nil
}

getValue := func(n string) string {
if sensor, err := hass.GetSensor(n); err == nil {
if details, err := i.tracker.Get(n); err == nil {
var valueStr strings.Builder

fmt.Fprintf(&valueStr, "%v", sensor.Value)
fmt.Fprintf(&valueStr, "%v", details.Value)

if sensor.Units != "" {
fmt.Fprintf(&valueStr, " %s", sensor.Units)
if details.Units != "" {
fmt.Fprintf(&valueStr, " %s", details.Units)
}

return valueStr.String()
Expand Down
18 changes: 14 additions & 4 deletions internal/cli/runCmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"github.com/joshuar/go-hass-agent/internal/agent"
"github.com/joshuar/go-hass-agent/internal/components/logging"
"github.com/joshuar/go-hass-agent/internal/components/preferences"
"github.com/joshuar/go-hass-agent/internal/components/registry"
"github.com/joshuar/go-hass-agent/internal/components/tracker"
"github.com/joshuar/go-hass-agent/internal/hass"
)

Expand All @@ -30,7 +32,9 @@ func (r *RunCmd) Run(opts *CmdOpts) error {
ctx, cancelFunc := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancelFunc()

// Load up the contenxt.
ctx = preferences.PathToCtx(ctx, opts.Path)
ctx = logging.ToContext(ctx, opts.Logger)

// Load the preferences from file. Ignore the case where there are no
// existing preferences.
Expand All @@ -40,17 +44,23 @@ func (r *RunCmd) Run(opts *CmdOpts) error {
return errors.Join(ErrRunCmdFailed, err)
}

// Load up the context for the agent.
ctx = logging.ToContext(ctx, opts.Logger)
// Load the registry.
reg, err := registry.Load(ctx)
if err != nil {
return errors.Join(ErrRunCmdFailed, err)
}

// Load the tracker.
trk := tracker.NewTracker()

// Create a new hass data handler.
dataCh, err := hass.NewDataHandler(ctx)
dataCh, err := hass.NewDataHandler(ctx, reg, trk)
if err != nil {
return errors.Join(ErrRunCmdFailed, err)
}

// Run the agent.
if err := agent.Run(ctx, dataCh); err != nil {
if err := agent.Run(ctx, dataCh, trk); err != nil {
return errors.Join(ErrRunCmdFailed, err)
}

Expand Down
64 changes: 28 additions & 36 deletions internal/hass/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
"log/slog"
"time"

"github.com/davecgh/go-spew/spew"

"github.com/joshuar/go-hass-agent/internal/components/logging"
"github.com/joshuar/go-hass-agent/internal/components/preferences"
"github.com/joshuar/go-hass-agent/internal/components/registry"
"github.com/joshuar/go-hass-agent/internal/components/tracker"
"github.com/joshuar/go-hass-agent/internal/hass/api"
"github.com/joshuar/go-hass-agent/internal/hass/event"
"github.com/joshuar/go-hass-agent/internal/hass/sensor"
Expand All @@ -23,11 +23,6 @@ const (
DefaultTimeout = 30 * time.Second
)

var (
sensorRegistry Registry
sensorTracker = tracker.NewTracker()
)

var (
ErrGetConfigFailed = errors.New("could not fetch Home Assistant config")
ErrGenRequestFailed = errors.New("unable to generate request for sensor")
Expand All @@ -48,33 +43,43 @@ var (
ErrInvalidSensor = errors.New("invalid sensor")
)

type Registry interface {
// sensorRegistry represents the required methods for hass to manage sensor
// registration state.
type sensorRegistry interface {
SetDisabled(id string, state bool) error
SetRegistered(id string, state bool) error
IsDisabled(id string) bool
IsRegistered(id string) bool
}

type handler struct {
logger *slog.Logger
// sensorTracker represents the required methods for hass to track sensors and
// their current state.
type sensorTracker interface {
SensorList() []string
Get(id string) (*sensor.Entity, error)
Add(details *sensor.Entity) error
}

func NewDataHandler(ctx context.Context) (chan any, error) {
var err error

sensorRegistry, err = registry.Load(ctx)
if err != nil {
return nil, fmt.Errorf("could not start registry: %w", err)
}
type handler struct {
logger *slog.Logger
registry sensorRegistry
tracker sensorTracker
}

func NewDataHandler(ctx context.Context, reg sensorRegistry, trk sensorTracker) (chan any, error) {
dataCh := make(chan any)

client := &handler{
logger: logging.FromContext(ctx).With(slog.String("subsystem", "hass")),
logger: logging.FromContext(ctx).With(slog.String("subsystem", "hass")),
registry: reg,
tracker: trk,
}

spew.Dump(ctx)

go func() {
for d := range dataCh {
var err error
switch data := d.(type) {
case sensor.Entity:
err = client.processSensor(ctx, data)
Expand Down Expand Up @@ -126,7 +131,7 @@ func (c *handler) processSensor(ctx context.Context, details sensor.Entity) erro
}

// Sensor update.
if sensorRegistry.IsRegistered(details.ID) {
if c.registry.IsRegistered(details.ID) {
// For sensor updates, if the sensor is disabled, don't continue.
if c.isDisabled(ctx, details) {
c.logger.
Expand All @@ -145,7 +150,7 @@ func (c *handler) processSensor(ctx context.Context, details sensor.Entity) erro
return fmt.Errorf("failed to send sensor update for %s: %w", details.Name, err)
}

go resp.Process(ctx, details)
go resp.Process(ctx, c.registry, c.tracker, details)

return nil
}
Expand All @@ -160,7 +165,7 @@ func (c *handler) processSensor(ctx context.Context, details sensor.Entity) erro
return fmt.Errorf("failed to send sensor registration: %w", err)
}

go resp.Process(ctx, details)
go resp.Process(ctx, c.registry, c.tracker, details)

return nil
}
Expand All @@ -183,7 +188,7 @@ func (c *handler) isDisabled(ctx context.Context, details sensor.Entity) bool {
c.logger.Info("Sensor re-enabled in Home Assistant, Re-enabling in local registry and sending updates.",
sensorLogAttrs(details))

if err := sensorRegistry.SetDisabled(details.ID, false); err != nil {
if err := c.registry.SetDisabled(details.ID, false); err != nil {
c.logger.Error("Could not re-enable sensor.",
sensorLogAttrs(details),
slog.Any("error", err))
Expand All @@ -204,7 +209,7 @@ func (c *handler) isDisabled(ctx context.Context, details sensor.Entity) bool {
//
//revive:disable:unused-receiver
func (c *handler) isDisabledInReg(id string) bool {
return sensorRegistry.IsDisabled(id)
return c.registry.IsDisabled(id)
}

// isDisabledInHA returns the disabled state of the sensor from Home Assistant.
Expand Down Expand Up @@ -232,19 +237,6 @@ func (c *handler) isDisabledInHA(ctx context.Context, details sensor.Entity) boo
return status
}

func GetSensor(id string) (*sensor.Entity, error) {
details, err := sensorTracker.Get(id)
if err != nil {
return nil, fmt.Errorf("could not get sensor details: %w", err)
}

return details, nil
}

func SensorList() []string {
return sensorTracker.SensorList()
}

// sensorLogAttrs is a convienience function that returns some slog attributes
// for priting sensor details in the log.
func sensorLogAttrs(details sensor.Entity) slog.Attr {
Expand Down
Loading

0 comments on commit 5c8534c

Please sign in to comment.