Skip to content

Commit

Permalink
refactor(agent,hass,tracker): rework api request/response logic
Browse files Browse the repository at this point in the history
- move parameters needed for rest api into context values
- simplify responses; either return an error or a specific type
- tracker logic streamlined for better response handling
  • Loading branch information
joshuar committed Sep 26, 2023
1 parent c6efde9 commit c4722fc
Show file tree
Hide file tree
Showing 21 changed files with 1,004 additions and 1,133 deletions.
47 changes: 31 additions & 16 deletions internal/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"syscall"

deviceConfig "github.com/joshuar/go-hass-agent/internal/config"
"github.com/joshuar/go-hass-agent/internal/hass/api"

"github.com/joshuar/go-hass-agent/internal/agent/config"
"github.com/joshuar/go-hass-agent/internal/agent/ui"
Expand Down Expand Up @@ -79,22 +80,26 @@ func Run(options AgentOptions) {
agent := newAgent(options.ID, options.Headless)
defer close(agent.done)

agentCtx, cancelFunc := context.WithCancel(context.Background())
agent.setupLogging(agentCtx)

agent.setupLogging()
registrationDone := make(chan struct{})
go agent.registrationProcess(agentCtx, "", "", options.Register, options.Headless, registrationDone)
configDone := make(chan struct{})

go agent.registrationProcess(context.Background(), "", "", options.Register, options.Headless, registrationDone)

var workerWg sync.WaitGroup
var ctx context.Context
var cancelFunc context.CancelFunc
go func() {
<-registrationDone
var err error
if err = UpgradeConfig(agent.config); err != nil {
log.Warn().Err(err).Msg("Could not start.")
log.Warn().Err(err).Msg("Could not upgrade config.")
}
if err = ValidateConfig(agent.config); err != nil {
log.Fatal().Err(err).Msg("Could not start.")
log.Fatal().Err(err).Msg("Could not validate config.")
}
ctx, cancelFunc = agent.setupContext()
close(configDone)
if agent.sensors, err = tracker.NewSensorTracker(agent); err != nil {
log.Fatal().Err(err).Msg("Could not start.")
}
Expand All @@ -104,24 +109,26 @@ func Run(options AgentOptions) {

workerWg.Add(1)
go func() {
device.StartWorkers(agentCtx, agent.sensors, sensorWorkers...)
device.StartWorkers(ctx, agent.sensors, sensorWorkers...)
}()
workerWg.Add(1)
go func() {
defer workerWg.Done()
agent.runNotificationsWorker(agentCtx, options)
agent.runNotificationsWorker(ctx, options)
}()
}()

<-configDone
agent.handleSignals(cancelFunc)
agent.handleShutdown(agentCtx)
agent.handleShutdown(ctx)

// If we are not running in headless mode, show a tray icon
if !options.Headless {
agent.ui.DisplayTrayIcon(agentCtx, agent)
agent.ui.DisplayTrayIcon(ctx, agent)
agent.ui.Run()
}
workerWg.Wait()
<-agentCtx.Done()
<-ctx.Done()
}

// Register runs a registration flow. It either prompts the user for needed
Expand Down Expand Up @@ -175,7 +182,7 @@ func ShowInfo(options AgentOptions) {

// setupLogging will attempt to create and then write logging to a file. If it
// cannot do this, logging will only be available on stdout
func (agent *Agent) setupLogging(ctx context.Context) {
func (agent *Agent) setupLogging() {
logFile, err := agent.config.StoragePath("go-hass-app.log")
if err != nil {
log.Error().Err(err).
Expand All @@ -189,14 +196,22 @@ func (agent *Agent) setupLogging(ctx context.Context) {
consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}
multiWriter := zerolog.MultiLevelWriter(consoleWriter, logWriter)
log.Logger = log.Output(multiWriter)
go func() {
<-ctx.Done()
logWriter.Close()
}()
}
}
}

func (agent *Agent) setupContext() (context.Context, context.CancelFunc) {
SharedConfig := &api.APIConfig{}
if err := agent.config.Get(config.PrefAPIURL, &SharedConfig.APIURL); err != nil {
log.Fatal().Err(err).Msg("Could not export apiURL.")
}
if err := agent.config.Get(config.PrefSecret, &SharedConfig.Secret); err != nil && SharedConfig.Secret != "NOTSET" {
log.Fatal().Err(err).Msg("Could not export secret.")
}
ctx := api.NewContext(context.Background(), SharedConfig)
return context.WithCancel(ctx)
}

// handleSignals will handle Ctrl-C of the agent
func (agent *Agent) handleSignals(cancelFunc context.CancelFunc) {
c := make(chan os.Signal, 1)
Expand Down
2 changes: 1 addition & 1 deletion internal/agent/ui/fyneUI.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func (ui *fyneUI) DisplayRegistrationWindow(ctx context.Context, agent Agent, do
// about the agent, such as version numbers.
func (ui *fyneUI) aboutWindow(ctx context.Context, agent Agent, t *translations.Translator) fyne.Window {
var widgets []fyne.CanvasObject
if hassConfig, err := hass.GetHassConfig(ctx, agent); err != nil {
if hassConfig, err := hass.GetHassConfig(ctx); err != nil {
widgets = append(widgets, widget.NewLabel(t.Translate(
"App Version: %s", agent.AppVersion())))
} else {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion internal/hass/api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package api

//go:generate moq -out mock_AgentConfig_test.go . AgentConfig
//go:generate moq -out mock_Agent_test.go . Agent
type Agent interface {
GetConfig(string, interface{}) error
}
33 changes: 33 additions & 0 deletions internal/hass/api/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2023 Joshua Rich <[email protected]>
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

package api

import "context"

type APIConfig struct {
APIURL string
Secret string
}

// key is an unexported type for keys defined in this package.
// This prevents collisions with keys defined in other packages.
type key int

// userKey is the key for user.User values in Contexts. It is
// unexported; clients use user.NewContext and user.FromContext
// instead of using this key directly.
var userKey key

// NewContext returns a new Context that carries value u.
func NewContext(ctx context.Context, c *APIConfig) context.Context {
return context.WithValue(ctx, userKey, c)
}

// FromContext returns the User value stored in ctx, if any.
func FromContext(ctx context.Context) (*APIConfig, bool) {
u, ok := ctx.Value(userKey).(*APIConfig)
return u, ok
}
80 changes: 80 additions & 0 deletions internal/hass/api/mock_Agent_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 104 additions & 0 deletions internal/hass/api/mock_RegistrationInfo_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c4722fc

Please sign in to comment.