Don't exit the app on connection failures, expose the error in the API

This commit is contained in:
Lubos Dolezel
2023-03-07 18:30:40 +01:00
parent 23f87fef8b
commit 754ffafeae
3 changed files with 31 additions and 11 deletions

13
app.go
View File

@@ -15,6 +15,9 @@ import (
ws "saml.dev/gome-assistant/internal/websocket" ws "saml.dev/gome-assistant/internal/websocket"
) )
// Returned by NewApp() if authentication fails
var ErrInvalidToken = ws.ErrInvalidToken
type App struct { type App struct {
ctx context.Context ctx context.Context
ctxCancel context.CancelFunc ctxCancel context.CancelFunc
@@ -77,7 +80,7 @@ type NewAppRequest struct {
NewApp establishes the websocket connection and returns an object NewApp establishes the websocket connection and returns an object
you can use to register schedules and listeners. you can use to register schedules and listeners.
*/ */
func NewApp(request NewAppRequest) *App { func NewApp(request NewAppRequest) (*App, error) {
if request.IpAddress == "" || request.HAAuthToken == "" || request.HomeZoneEntityId == "" { if request.IpAddress == "" || request.HAAuthToken == "" || request.HomeZoneEntityId == "" {
log.Fatalln("IpAddress, HAAuthToken, and HomeZoneEntityId are all required arguments in NewAppRequest.") log.Fatalln("IpAddress, HAAuthToken, and HomeZoneEntityId are all required arguments in NewAppRequest.")
} }
@@ -85,7 +88,11 @@ func NewApp(request NewAppRequest) *App {
if port == "" { if port == "" {
port = "8123" port = "8123"
} }
conn, ctx, ctxCancel := ws.SetupConnection(request.IpAddress, port, request.HAAuthToken) conn, ctx, ctxCancel, err := ws.SetupConnection(request.IpAddress, port, request.HAAuthToken)
if conn == nil {
return nil, err
}
httpClient := http.NewHttpClient(request.IpAddress, port, request.HAAuthToken) httpClient := http.NewHttpClient(request.IpAddress, port, request.HAAuthToken)
@@ -105,7 +112,7 @@ func NewApp(request NewAppRequest) *App {
intervals: pq.New(), intervals: pq.New(),
entityListeners: map[string][]*EntityListener{}, entityListeners: map[string][]*EntityListener{},
eventListeners: map[string][]*EventListener{}, eventListeners: map[string][]*EventListener{},
} }, nil
} }
func (a *App) Cleanup() { func (a *App) Cleanup() {

View File

@@ -10,11 +10,16 @@ import (
) )
func main() { func main() {
app := ga.NewApp(ga.NewAppRequest{ app, err := ga.NewApp(ga.NewAppRequest{
IpAddress: "192.168.86.67", // Replace with your Home Assistant IP Address IpAddress: "192.168.86.67", // Replace with your Home Assistant IP Address
HAAuthToken: os.Getenv("HA_AUTH_TOKEN"), HAAuthToken: os.Getenv("HA_AUTH_TOKEN"),
HomeZoneEntityId: "zone.home", HomeZoneEntityId: "zone.home",
}) })
if err != nil {
log.Fatalln("Error connecting to HASS:", err)
}
defer app.Cleanup() defer app.Cleanup()
pantryDoor := ga. pantryDoor := ga.

View File

@@ -17,6 +17,10 @@ import (
i "saml.dev/gome-assistant/internal" i "saml.dev/gome-assistant/internal"
) )
var (
ErrInvalidToken = errors.New("invalid authentication token")
)
type AuthMessage struct { type AuthMessage struct {
MsgType string `json:"type"` MsgType string `json:"type"`
AccessToken string `json:"access_token"` AccessToken string `json:"access_token"`
@@ -47,7 +51,7 @@ func ReadMessage(conn *websocket.Conn, ctx context.Context) ([]byte, error) {
return msg, nil return msg, nil
} }
func SetupConnection(ip, port, authToken string) (*websocket.Conn, context.Context, context.CancelFunc) { func SetupConnection(ip, port, authToken string) (*websocket.Conn, context.Context, context.CancelFunc, error) {
ctx, ctxCancel := context.WithTimeout(context.Background(), time.Second*3) ctx, ctxCancel := context.WithTimeout(context.Background(), time.Second*3)
// Init websocket connection // Init websocket connection
@@ -55,31 +59,35 @@ func SetupConnection(ip, port, authToken string) (*websocket.Conn, context.Conte
conn, _, err := dialer.DialContext(ctx, fmt.Sprintf("ws://%s:%s/api/websocket", ip, port), nil) conn, _, err := dialer.DialContext(ctx, fmt.Sprintf("ws://%s:%s/api/websocket", ip, port), nil)
if err != nil { if err != nil {
ctxCancel() ctxCancel()
log.Fatalf("ERROR: Failed to connect to websocket at ws://%s:%s/api/websocket. Check IP address and port\n", ip, port) log.Printf("ERROR: Failed to connect to websocket at ws://%s:%s/api/websocket. Check IP address and port\n", ip, port)
return nil, nil, nil, err
} }
// Read auth_required message // Read auth_required message
_, err = ReadMessage(conn, ctx) _, err = ReadMessage(conn, ctx)
if err != nil { if err != nil {
ctxCancel() ctxCancel()
log.Fatalf("Unknown error creating websocket client\n") log.Printf("Unknown error creating websocket client\n")
return nil, nil, nil, err
} }
// Send auth message // Send auth message
err = SendAuthMessage(conn, ctx, authToken) err = SendAuthMessage(conn, ctx, authToken)
if err != nil { if err != nil {
ctxCancel() ctxCancel()
log.Fatalf("Unknown error creating websocket client\n") log.Printf("Unknown error creating websocket client\n")
return nil, nil, nil, err
} }
// Verify auth message was successful // Verify auth message was successful
err = VerifyAuthResponse(conn, ctx) err = VerifyAuthResponse(conn, ctx)
if err != nil { if err != nil {
ctxCancel() ctxCancel()
log.Fatalf("ERROR: Auth token is invalid. Please double check it or create a new token in your Home Assistant profile\n") log.Printf("ERROR: Auth token is invalid. Please double check it or create a new token in your Home Assistant profile\n")
return nil, nil, nil, err
} }
return conn, ctx, ctxCancel return conn, ctx, ctxCancel, nil
} }
func SendAuthMessage(conn *websocket.Conn, ctx context.Context, token string) error { func SendAuthMessage(conn *websocket.Conn, ctx context.Context, token string) error {
@@ -105,7 +113,7 @@ func VerifyAuthResponse(conn *websocket.Conn, ctx context.Context) error {
json.Unmarshal(msg, &authResp) json.Unmarshal(msg, &authResp)
// log.Println(authResp.MsgType) // log.Println(authResp.MsgType)
if authResp.MsgType != "auth_ok" { if authResp.MsgType != "auth_ok" {
return errors.New("invalid auth token") return ErrInvalidToken
} }
return nil return nil