Skip to content

Commit 24f814b

Browse files
authored
Merge pull request #23 from metalmatze/url
Parse URL and pass it to clients
2 parents 0664417 + b5d3523 commit 24f814b

File tree

7 files changed

+67
-71
lines changed

7 files changed

+67
-71
lines changed

app.go

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,19 @@ import (
55
"errors"
66
"fmt"
77
"log/slog"
8+
"net/url"
89
"time"
910

1011
"github.com/golang-module/carbon"
1112
"github.com/gorilla/websocket"
1213
sunriseLib "github.com/nathan-osman/go-sunrise"
14+
1315
"saml.dev/gome-assistant/internal"
1416
"saml.dev/gome-assistant/internal/http"
1517
pq "saml.dev/gome-assistant/internal/priorityqueue"
1618
ws "saml.dev/gome-assistant/internal/websocket"
1719
)
1820

19-
// Returned by NewApp() if authentication fails
20-
var ErrInvalidToken = ws.ErrInvalidToken
21-
2221
var ErrInvalidArgs = errors.New("invalid arguments provided")
2322

2423
type App struct {
@@ -41,15 +40,11 @@ type App struct {
4140
eventListeners map[string][]*EventListener
4241
}
4342

44-
/*
45-
DurationString represents a duration, such as "2s" or "24h".
46-
See https://pkg.go.dev/time#ParseDuration for all valid time units.
47-
*/
43+
// DurationString represents a duration, such as "2s" or "24h".
44+
// See https://pkg.go.dev/time#ParseDuration for all valid time units.
4845
type DurationString string
4946

50-
/*
51-
TimeString is a 24-hr format time "HH:MM" such as "07:30".
52-
*/
47+
// TimeString is a 24-hr format time "HH:MM" such as "07:30".
5348
type TimeString string
5449

5550
type timeRange struct {
@@ -59,11 +54,16 @@ type timeRange struct {
5954

6055
type NewAppRequest struct {
6156
// Required
57+
URL string
58+
59+
// Optional
60+
// Deprecated: use URL instead
6261
// IpAddress of your Home Assistant instance i.e. "localhost"
6362
// or "192.168.86.59" etc.
6463
IpAddress string
6564

6665
// Optional
66+
// Deprecated: use URL instead
6767
// Port number Home Assistant is running on. Defaults to 8123.
6868
Port string
6969

@@ -90,39 +90,37 @@ NewApp establishes the websocket connection and returns an object
9090
you can use to register schedules and listeners.
9191
*/
9292
func NewApp(request NewAppRequest) (*App, error) {
93-
if request.IpAddress == "" || request.HAAuthToken == "" || request.HomeZoneEntityId == "" {
94-
slog.Error("IpAddress, HAAuthToken, and HomeZoneEntityId are all required arguments in NewAppRequest")
93+
if (request.URL == "" && request.IpAddress == "") || request.HAAuthToken == "" || request.HomeZoneEntityId == "" {
94+
slog.Error("URL, HAAuthToken, and HomeZoneEntityId are all required arguments in NewAppRequest")
9595
return nil, ErrInvalidArgs
9696
}
97-
port := request.Port
98-
if port == "" {
99-
port = "8123"
100-
}
10197

102-
var (
103-
conn *websocket.Conn
104-
ctx context.Context
105-
ctxCancel context.CancelFunc
106-
err error
107-
)
98+
baseURL := &url.URL{}
10899

109-
if request.Secure {
110-
conn, ctx, ctxCancel, err = ws.SetupSecureConnection(request.IpAddress, port, request.HAAuthToken)
100+
if request.URL != "" {
101+
var err error
102+
baseURL, err = url.Parse(request.URL)
103+
if err != nil {
104+
return nil, ErrInvalidArgs
105+
}
111106
} else {
112-
conn, ctx, ctxCancel, err = ws.SetupConnection(request.IpAddress, port, request.HAAuthToken)
107+
// This is deprecated and will be removed in a future release
108+
port := request.Port
109+
if port == "" {
110+
port = "8123"
111+
}
112+
baseURL.Host = request.IpAddress + ":" + port
113113
}
114114

115+
conn, ctx, ctxCancel, err := ws.ConnectionFromUri(baseURL, request.HAAuthToken)
116+
if err != nil {
117+
return nil, err
118+
}
115119
if conn == nil {
116120
return nil, err
117121
}
118122

119-
var httpClient *http.HttpClient
120-
121-
if request.Secure {
122-
httpClient = http.NewHttpsClient(request.IpAddress, port, request.HAAuthToken)
123-
} else {
124-
httpClient = http.NewHttpClient(request.IpAddress, port, request.HAAuthToken)
125-
}
123+
httpClient := http.NewHttpClient(baseURL, request.HAAuthToken)
126124

127125
wsWriter := &ws.WebsocketWriter{Conn: conn}
128126
service := newService(wsWriter, ctx, httpClient)

example/example.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package example
1+
package main
22

33
import (
44
"encoding/json"
@@ -11,12 +11,12 @@ import (
1111

1212
func main() {
1313
app, err := ga.NewApp(ga.NewAppRequest{
14-
IpAddress: "192.168.86.67", // Replace with your Home Assistant IP Address
14+
URL: "http://192.168.86.67:8123", // Replace with your Home Assistant IP Address
1515
HAAuthToken: os.Getenv("HA_AUTH_TOKEN"),
1616
HomeZoneEntityId: "zone.home",
1717
})
1818
if err != nil {
19-
slog.Error("Error connecting to HASS:", err)
19+
slog.Error("Error connecting to HASS:", "error", err)
2020
os.Exit(1)
2121
}
2222

example/example_live_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package example
1+
package main
22

33
import (
44
"log/slog"
@@ -10,6 +10,7 @@ import (
1010
"github.com/stretchr/testify/assert"
1111
"github.com/stretchr/testify/suite"
1212
"gopkg.in/yaml.v3"
13+
1314
ga "saml.dev/gome-assistant"
1415
)
1516

@@ -52,13 +53,13 @@ func (s *MySuite) SetupSuite() {
5253

5354
configFile, err := os.ReadFile("./config.yaml")
5455
if err != nil {
55-
slog.Error("Error reading config file", err)
56+
slog.Error("Error reading config file", "error", err)
5657
}
5758
s.config = &Config{}
5859
// either env var or config file can be used to set HA auth. token
5960
s.config.Hass.HAAuthToken = os.Getenv("HA_AUTH_TOKEN")
6061
if err := yaml.Unmarshal(configFile, s.config); err != nil {
61-
slog.Error("Error unmarshalling config file", err)
62+
slog.Error("Error unmarshalling config file", "error", err)
6263
}
6364

6465
s.app, err = ga.NewApp(ga.NewAppRequest{
@@ -67,7 +68,7 @@ func (s *MySuite) SetupSuite() {
6768
HomeZoneEntityId: s.config.Hass.HomeZoneEntityId,
6869
})
6970
if err != nil {
70-
slog.Error("Failed to createw new app", err)
71+
slog.Error("Failed to create new app", "error", err)
7172
s.T().FailNow()
7273
}
7374

@@ -135,7 +136,7 @@ func (s *MySuite) dailyScheduleCallback(se *ga.Service, st ga.State) {
135136
func getEntityState(s *MySuite, entityId string) string {
136137
state, err := s.app.GetState().Get(entityId)
137138
if err != nil {
138-
slog.Error("Error getting entity state", err)
139+
slog.Error("Error getting entity state", "error", err)
139140
s.T().FailNow()
140141
}
141142
slog.Info("State of entity", "state", state.State)

example/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module example
22

3-
go 1.21
3+
go 1.23
44

55
require (
66
github.com/golang-cz/devslog v0.0.8

internal/http/http.go

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,29 @@ package http
55

66
import (
77
"errors"
8-
"fmt"
98
"io"
109
"net/http"
10+
"net/url"
1111
)
1212

1313
type HttpClient struct {
1414
url string
1515
token string
1616
}
1717

18-
func NewHttpClient(ip, port, token string) *HttpClient {
19-
return ClientFromUri(
20-
fmt.Sprintf("http://%s:%s/api", ip, port),
21-
token,
22-
)
23-
}
24-
25-
func NewHttpsClient(ip, port, token string) *HttpClient {
26-
return ClientFromUri(
27-
fmt.Sprintf("https://%s:%s/api", ip, port),
28-
token,
29-
)
30-
}
18+
func NewHttpClient(url *url.URL, token string) *HttpClient {
19+
// Shallow copy the URL to avoid modifying the original
20+
u := *url
21+
if u.Scheme == "ws" {
22+
u.Scheme = "http"
23+
}
24+
if u.Scheme == "wss" {
25+
u.Scheme = "https"
26+
}
3127

32-
func ClientFromUri(uri, token string) *HttpClient {
3328
return &HttpClient{
34-
uri,
35-
token,
29+
url: u.String(),
30+
token: token,
3631
}
3732
}
3833

internal/websocket/websocket.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ import (
1010
"errors"
1111
"fmt"
1212
"log/slog"
13+
"net/url"
1314
"sync"
1415
"time"
1516

1617
"github.com/gorilla/websocket"
18+
1719
i "saml.dev/gome-assistant/internal"
1820
)
1921

@@ -49,25 +51,24 @@ func ReadMessage(conn *websocket.Conn, ctx context.Context) ([]byte, error) {
4951
return msg, nil
5052
}
5153

52-
func SetupConnection(ip, port, authToken string) (*websocket.Conn, context.Context, context.CancelFunc, error) {
53-
uri := fmt.Sprintf("ws://%s:%s/api/websocket", ip, port)
54-
return ConnectionFromUri(uri, authToken)
55-
}
56-
57-
func SetupSecureConnection(ip, port, authToken string) (*websocket.Conn, context.Context, context.CancelFunc, error) {
58-
uri := fmt.Sprintf("wss://%s:%s/api/websocket", ip, port)
59-
return ConnectionFromUri(uri, authToken)
60-
}
61-
62-
func ConnectionFromUri(uri, authToken string) (*websocket.Conn, context.Context, context.CancelFunc, error) {
54+
func ConnectionFromUri(baseURL *url.URL, authToken string) (*websocket.Conn, context.Context, context.CancelFunc, error) {
6355
ctx, ctxCancel := context.WithTimeout(context.Background(), time.Second*3)
6456

57+
// Shallow copy the URL to avoid modifying the original
58+
urlWebsockets := *baseURL
59+
if baseURL.Scheme == "http" {
60+
urlWebsockets.Scheme = "ws"
61+
}
62+
if baseURL.Scheme == "https" {
63+
urlWebsockets.Scheme = "wss"
64+
}
65+
6566
// Init websocket connection
6667
dialer := websocket.DefaultDialer
67-
conn, _, err := dialer.DialContext(ctx, uri, nil)
68+
conn, _, err := dialer.DialContext(ctx, urlWebsockets.String(), nil)
6869
if err != nil {
6970
ctxCancel()
70-
slog.Error("Failed to connect to websocket. Check URI\n", "uri", uri)
71+
slog.Error("Failed to connect to websocket. Check URI\n", "url", urlWebsockets)
7172
return nil, nil, nil, err
7273
}
7374

state.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/golang-module/carbon"
10+
1011
"saml.dev/gome-assistant/internal/http"
1112
)
1213

0 commit comments

Comments
 (0)