refactor: websockets into 'connect' module, rename & adjust generally

This commit is contained in:
2025-08-01 20:34:45 -05:00
parent 102a4e7438
commit 9b8ef545a6
32 changed files with 202 additions and 149 deletions

30
app.go
View File

@@ -14,8 +14,8 @@ import (
sunriseLib "github.com/nathan-osman/go-sunrise" sunriseLib "github.com/nathan-osman/go-sunrise"
"github.com/Workiva/go-datastructures/queue" "github.com/Workiva/go-datastructures/queue"
internal "github.com/Xevion/go-ha/internal" "github.com/Xevion/go-ha/internal"
ws "github.com/Xevion/go-ha/internal/websocket" "github.com/Xevion/go-ha/internal/connect"
"github.com/Xevion/go-ha/types" "github.com/Xevion/go-ha/types"
) )
@@ -24,10 +24,9 @@ var ErrInvalidArgs = errors.New("invalid arguments provided")
type App struct { type App struct {
ctx context.Context ctx context.Context
ctxCancel context.CancelFunc ctxCancel context.CancelFunc
conn *websocket.Conn
// Wraps the ws connection with added mutex locking // Wraps the ws connection with added mutex locking
wsWriter *ws.WebsocketWriter conn *connect.HAConnection
httpClient *internal.HttpClient httpClient *internal.HttpClient
@@ -101,18 +100,14 @@ func NewApp(request types.NewAppRequest) (*App, error) {
} }
} }
conn, ctx, ctxCancel, err := ws.ConnectionFromUri(baseURL, request.HAAuthToken) conn, ctx, ctxCancel, err := connect.ConnectionFromUri(baseURL, request.HAAuthToken)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if conn == nil {
return nil, err
}
httpClient := internal.NewHttpClient(baseURL, request.HAAuthToken) httpClient := internal.NewHttpClient(ctx, baseURL, request.HAAuthToken)
wsWriter := &ws.WebsocketWriter{Conn: conn} service := newService(conn)
service := newService(wsWriter)
state, err := newState(httpClient, request.HomeZoneEntityId) state, err := newState(httpClient, request.HomeZoneEntityId)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -125,7 +120,6 @@ func NewApp(request types.NewAppRequest) (*App, error) {
return &App{ return &App{
conn: conn, conn: conn,
wsWriter: wsWriter,
ctx: ctx, ctx: ctx,
ctxCancel: ctxCancel, ctxCancel: ctxCancel,
httpClient: httpClient, httpClient: httpClient,
@@ -151,14 +145,14 @@ func (app *App) Close() error {
// Close websocket connection if it exists // Close websocket connection if it exists
if app.conn != nil { if app.conn != nil {
deadline := time.Now().Add(10 * time.Second) deadline := time.Now().Add(10 * time.Second)
err := app.conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), deadline) err := app.conn.Conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), deadline)
if err != nil { if err != nil {
slog.Warn("Error writing close message", "error", err) slog.Warn("Error writing close message", "error", err)
return err return err
} }
// Close the websocket connection // Close the websocket connection
err = app.conn.Close() err = app.conn.Conn.Close()
if err != nil { if err != nil {
slog.Warn("Error closing websocket connection", "error", err) slog.Warn("Error closing websocket connection", "error", err)
return err return err
@@ -249,7 +243,7 @@ func (app *App) RegisterEventListeners(evls ...EventListener) {
if elList, ok := app.eventListeners[eventType]; ok { if elList, ok := app.eventListeners[eventType]; ok {
app.eventListeners[eventType] = append(elList, &evl) app.eventListeners[eventType] = append(elList, &evl)
} else { } else {
ws.SubscribeToEventType(eventType, app.wsWriter, app.ctx) connect.SubscribeToEventType(eventType, app.conn, app.ctx)
app.eventListeners[eventType] = []*EventListener{&evl} app.eventListeners[eventType] = []*EventListener{&evl}
} }
} }
@@ -309,7 +303,7 @@ func (app *App) Start() {
// subscribe to state_changed events // subscribe to state_changed events
id := internal.NextId() id := internal.NextId()
ws.SubscribeToStateChangedEvents(id, app.wsWriter, app.ctx) connect.SubscribeToStateChangedEvents(id, app.conn, app.ctx)
app.entityListenersId = id app.entityListenersId = id
// Run entity listeners startup // Run entity listeners startup
@@ -337,8 +331,8 @@ func (app *App) Start() {
} }
// entity listeners and event listeners // entity listeners and event listeners
elChan := make(chan ws.ChanMsg, 100) // Add buffer to prevent channel overflow elChan := make(chan connect.ChannelMessage, 100) // Add buffer to prevent channel overflow
go ws.ListenWebsocket(app.conn, elChan) go connect.ListenWebsocket(app.conn.Conn, elChan)
for { for {
select { select {

View File

@@ -8,7 +8,7 @@ import (
"github.com/golang-module/carbon" "github.com/golang-module/carbon"
"github.com/Xevion/go-ha/internal" "github.com/Xevion/go-ha/internal"
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
"github.com/Xevion/go-ha/types" "github.com/Xevion/go-ha/types"
) )
@@ -141,7 +141,7 @@ type BaseEventMsg struct {
} }
/* Functions */ /* Functions */
func callEventListeners(app *App, msg ws.ChanMsg) { func callEventListeners(app *App, msg ws.ChannelMessage) {
baseEventMsg := BaseEventMsg{} baseEventMsg := BaseEventMsg{}
_ = json.Unmarshal(msg.Raw, &baseEventMsg) _ = json.Unmarshal(msg.Raw, &baseEventMsg)
listeners, ok := app.eventListeners[baseEventMsg.Event.EventType] listeners, ok := app.eventListeners[baseEventMsg.Event.EventType]

View File

@@ -2,7 +2,7 @@
// websocket API. All HA interaction is done via websocket // websocket API. All HA interaction is done via websocket
// except for cases explicitly called out in http package // except for cases explicitly called out in http package
// documentation. // documentation.
package websocket package connect
import ( import (
"context" "context"
@@ -20,46 +20,60 @@ import (
var ErrInvalidToken = errors.New("invalid authentication token") var ErrInvalidToken = errors.New("invalid authentication token")
type AuthMessage struct { // HAConnection is a wrapper around a websocket connection that provides a mutex for thread safety.
MsgType string `json:"type"` type HAConnection struct {
AccessToken string `json:"access_token"` Conn *websocket.Conn // Note: this is not thread safe except for Close() and WriteControl()
}
type WebsocketWriter struct {
Conn *websocket.Conn
mutex sync.Mutex mutex sync.Mutex
} }
func (w *WebsocketWriter) WriteMessage(msg any) error { // WriteMessage writes a message to the websocket connection.
func (w *HAConnection) WriteMessage(msg any) error {
w.mutex.Lock() w.mutex.Lock()
defer w.mutex.Unlock() defer w.mutex.Unlock()
return w.Conn.WriteJSON(msg) return w.Conn.WriteJSON(msg)
} }
func ReadMessage(conn *websocket.Conn) ([]byte, error) { // ReadMessageRaw reads a raw message from the websocket connection.
func ReadMessageRaw(conn *websocket.Conn) ([]byte, error) {
_, msg, err := conn.ReadMessage() _, msg, err := conn.ReadMessage()
if err != nil { if err != nil {
return []byte{}, err return nil, err
} }
return msg, nil return msg, nil
} }
func ConnectionFromUri(baseURL *url.URL, authToken string) (*websocket.Conn, context.Context, context.CancelFunc, error) { // ReadMessage reads a message from the websocket connection and unmarshals it into the given type.
func ReadMessage[T any](conn *websocket.Conn) (T, error) {
var result T
_, msg, err := conn.ReadMessage()
if err != nil {
return result, err
}
err = json.Unmarshal(msg, &result)
if err != nil {
return result, err
}
return result, nil
}
// ConnectionFromUri creates a new websocket connection from the given base URL and authentication token.
func ConnectionFromUri(baseUrl *url.URL, token string) (*HAConnection, context.Context, context.CancelFunc, error) {
// Build the websocket URL
urlWebsockets := *baseUrl
urlWebsockets.Path = "/api/websocket"
scheme, err := internal.GetEquivalentWebsocketScheme(baseUrl.Scheme)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to build websocket URL: %w", err)
}
urlWebsockets.Scheme = scheme
// Create a short timeout context for the connection only // Create a short timeout context for the connection only
connCtx, connCtxCancel := context.WithTimeout(context.Background(), time.Second*3) connCtx, connCtxCancel := context.WithTimeout(context.Background(), time.Second*3)
defer connCtxCancel() // Always cancel the connection context when we're done defer connCtxCancel() // Always cancel the connection context when we're done
// Shallow copy the URL to avoid modifying the original
urlWebsockets := *baseURL
urlWebsockets.Path = "/api/websocket"
if baseURL.Scheme == "http" {
urlWebsockets.Scheme = "ws"
}
if baseURL.Scheme == "https" {
urlWebsockets.Scheme = "wss"
}
// Init websocket connection // Init websocket connection
dialer := websocket.DefaultDialer dialer := websocket.DefaultDialer
conn, _, err := dialer.DialContext(connCtx, urlWebsockets.String(), nil) conn, _, err := dialer.DialContext(connCtx, urlWebsockets.String(), nil)
@@ -69,14 +83,19 @@ func ConnectionFromUri(baseURL *url.URL, authToken string) (*websocket.Conn, con
} }
// Read auth_required message // Read auth_required message
_, err = ReadMessage(conn) msg, err := ReadMessage[struct {
MsgType string `json:"type"`
}](conn)
if err != nil { if err != nil {
slog.Error("Unknown error creating websocket client\n") slog.Error("Unknown error creating websocket client\n")
return nil, nil, nil, err return nil, nil, nil, err
} else if msg.MsgType != "auth_required" {
slog.Error("Expected auth_required message, got", "msgType", msg.MsgType)
return nil, nil, nil, fmt.Errorf("expected auth_required message, got %s", msg.MsgType)
} }
// Send auth message // Send auth message
err = SendAuthMessage(conn, connCtx, authToken) err = SendAuthMessage(conn, connCtx, token)
if err != nil { if err != nil {
slog.Error("Unknown error creating websocket client\n") slog.Error("Unknown error creating websocket client\n")
return nil, nil, nil, err return nil, nil, nil, err
@@ -92,51 +111,54 @@ func ConnectionFromUri(baseURL *url.URL, authToken string) (*websocket.Conn, con
// Create a new background context for the application lifecycle (no timeout) // Create a new background context for the application lifecycle (no timeout)
appCtx, appCtxCancel := context.WithCancel(context.Background()) appCtx, appCtxCancel := context.WithCancel(context.Background())
return conn, appCtx, appCtxCancel, nil return &HAConnection{Conn: conn}, appCtx, appCtxCancel, nil
} }
// SendAuthMessage sends an auth message to the websocket connection.
func SendAuthMessage(conn *websocket.Conn, ctx context.Context, token string) error { func SendAuthMessage(conn *websocket.Conn, ctx context.Context, token string) error {
type AuthMessage struct {
MsgType string `json:"type"`
AccessToken string `json:"access_token"`
}
err := conn.WriteJSON(AuthMessage{MsgType: "auth", AccessToken: token}) err := conn.WriteJSON(AuthMessage{MsgType: "auth", AccessToken: token})
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
type authResponse struct { // VerifyAuthResponse verifies that the auth response is valid.
func VerifyAuthResponse(conn *websocket.Conn, ctx context.Context) error {
msg, err := ReadMessage[struct {
MsgType string `json:"type"` MsgType string `json:"type"`
Message string `json:"message"` Message string `json:"message"`
} }](conn)
func VerifyAuthResponse(conn *websocket.Conn, ctx context.Context) error {
msg, err := ReadMessage(conn)
if err != nil { if err != nil {
return err return err
} }
var authResp authResponse if msg.MsgType != "auth_ok" {
err = json.Unmarshal(msg, &authResp)
if err != nil {
return err
}
if authResp.MsgType != "auth_ok" {
return ErrInvalidToken return ErrInvalidToken
} }
return nil return nil
} }
func SubscribeToStateChangedEvents(id int64, conn *HAConnection, ctx context.Context) {
SubscribeToEventType("state_changed", conn, ctx, id)
}
// TODO: Instead of using variadic arguments, just use a nillable pointer for the id
func SubscribeToEventType(eventType string, conn *HAConnection, ctx context.Context, id ...int64) {
type SubEvent struct { type SubEvent struct {
Id int64 `json:"id"` Id int64 `json:"id"`
Type string `json:"type"` Type string `json:"type"`
EventType string `json:"event_type"` EventType string `json:"event_type"`
} }
func SubscribeToStateChangedEvents(id int64, conn *WebsocketWriter, ctx context.Context) { // If no id is provided, generate a new one
SubscribeToEventType("state_changed", conn, ctx, id)
}
func SubscribeToEventType(eventType string, conn *WebsocketWriter, ctx context.Context, id ...int64) {
var finalId int64 var finalId int64
if len(id) == 0 { if len(id) == 0 {
finalId = internal.NextId() finalId = internal.NextId()
@@ -148,12 +170,12 @@ func SubscribeToEventType(eventType string, conn *WebsocketWriter, ctx context.C
Type: "subscribe_events", Type: "subscribe_events",
EventType: eventType, EventType: eventType,
} }
err := conn.WriteMessage(e) err := conn.WriteMessage(e)
// TODO: Handle errors better
if err != nil { if err != nil {
wrappedErr := fmt.Errorf("error writing to websocket: %w", err) wrappedErr := fmt.Errorf("error writing to websocket: %w", err)
slog.Error(wrappedErr.Error()) slog.Error(wrappedErr.Error())
panic(wrappedErr) panic(wrappedErr)
} }
// m, _ := ReadMessage(conn, ctx)
// log.Default().Println(string(m))
} }

View File

@@ -1,4 +1,4 @@
package websocket package connect
import ( import (
"encoding/json" "encoding/json"
@@ -7,22 +7,26 @@ import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
// BaseMessage is the base message type for all messages sent by the websocket server.
type BaseMessage struct { type BaseMessage struct {
Type string `json:"type"` Type string `json:"type"`
Id int64 `json:"id"` Id int64 `json:"id"`
Success bool `json:"success"` Success bool `json:"success"` // not present in all messages
} }
type ChanMsg struct { type ChannelMessage struct {
Id int64 Id int64
Type string Type string
Success bool Success bool
Raw []byte Raw []byte
} }
func ListenWebsocket(conn *websocket.Conn, c chan ChanMsg) { // ListenWebsocket reads messages from the websocket connection and sends them to the channel.
// It will close the channel if it encounters an error, or if the channel is full, and return.
// It ignores errors in deserialization.
func ListenWebsocket(conn *websocket.Conn, c chan ChannelMessage) {
for { for {
bytes, err := ReadMessage(conn) raw, err := ReadMessageRaw(conn)
if err != nil { if err != nil {
slog.Error("Error reading from websocket", "err", err) slog.Error("Error reading from websocket", "err", err)
close(c) close(c)
@@ -33,20 +37,26 @@ func ListenWebsocket(conn *websocket.Conn, c chan ChanMsg) {
// default to true for messages that don't include "success" at all // default to true for messages that don't include "success" at all
Success: true, Success: true,
} }
_ = json.Unmarshal(bytes, &base) err = json.Unmarshal(raw, &base)
if !base.Success { if err != nil {
slog.Warn("Received unsuccessful response", "response", string(bytes)) slog.Error("Error unmarshalling message", "err", err, "message", string(raw))
continue
} }
chanMsg := ChanMsg{ if !base.Success {
slog.Warn("Received unsuccessful response", "response", string(raw))
}
// Create a channel message from the raw message
channelMessage := ChannelMessage{
Type: base.Type, Type: base.Type,
Id: base.Id, Id: base.Id,
Success: base.Success, Success: base.Success,
Raw: bytes, Raw: raw,
} }
// Use non-blocking send to avoid hanging on closed channel // Use non-blocking send to avoid hanging on closed channel
select { select {
case c <- chanMsg: case c <- channelMessage:
// Message sent successfully // Message sent successfully
default: default:
// Channel is full or closed, break out of loop // Channel is full or closed, break out of loop

View File

@@ -4,6 +4,7 @@
package internal package internal
import ( import (
"context"
"errors" "errors"
"net/url" "net/url"
"time" "time"
@@ -13,39 +14,43 @@ import (
type HttpClient struct { type HttpClient struct {
client *resty.Client client *resty.Client
baseRequest *resty.Request
} }
func NewHttpClient(url *url.URL, token string) *HttpClient { func NewHttpClient(ctx context.Context, baseUrl *url.URL, token string) *HttpClient {
// Shallow copy the URL to avoid modifying the original // Shallow copy the URL to avoid modifying the original
u := *url u := *baseUrl
u.Path = "/api" u.Path = "/api"
if u.Scheme == "ws" {
u.Scheme = "http"
}
if u.Scheme == "wss" {
u.Scheme = "https"
}
// Create resty client with configuration // Create resty client with configuration
client := resty.New(). client := resty.New().
SetBaseURL(u.String()). SetBaseURL(u.String()).
SetHeader("Authorization", "Bearer "+token).
SetTimeout(30*time.Second). SetTimeout(30*time.Second).
SetRetryCount(3). SetRetryCount(3).
SetRetryWaitTime(1*time.Second). SetRetryWaitTime(1*time.Second).
SetRetryMaxWaitTime(5*time.Second). SetRetryMaxWaitTime(5*time.Second).
AddRetryConditions(func(r *resty.Response, err error) bool { AddRetryConditions(func(r *resty.Response, err error) bool {
return err != nil || r.StatusCode() >= 500 return err != nil || (r.StatusCode() >= 500 && r.StatusCode() != 403)
}) }).
SetHeader("User-Agent", "go-ha/"+currentVersion).
SetContext(ctx)
return &HttpClient{ return &HttpClient{
client: client, client: client,
baseRequest: client.R().
SetContentType("application/json").
SetHeader("Accept", "application/json").
SetAuthToken(token),
} }
} }
// getRequest returns a new request
func (c *HttpClient) getRequest() *resty.Request {
return c.baseRequest.Clone(c.client.Context())
}
func (c *HttpClient) GetState(entityId string) ([]byte, error) { func (c *HttpClient) GetState(entityId string) ([]byte, error) {
resp, err := c.client.R(). resp, err := c.getRequest().Get("/states/" + entityId)
Get("/states/" + entityId)
if err != nil { if err != nil {
return nil, errors.New("Error making HTTP request: " + err.Error()) return nil, errors.New("Error making HTTP request: " + err.Error())
@@ -58,9 +63,9 @@ func (c *HttpClient) GetState(entityId string) ([]byte, error) {
return resp.Bytes(), nil return resp.Bytes(), nil
} }
func (c *HttpClient) States() ([]byte, error) { // GetStates returns the states of all entities.
resp, err := c.client.R(). func (c *HttpClient) GetStates() ([]byte, error) {
Get("/states") resp, err := c.getRequest().Get("/states")
if err != nil { if err != nil {
return nil, errors.New("Error making HTTP request: " + err.Error()) return nil, errors.New("Error making HTTP request: " + err.Error())

View File

@@ -1,6 +1,7 @@
package internal package internal
import ( import (
"fmt"
"reflect" "reflect"
"runtime" "runtime"
"sync/atomic" "sync/atomic"
@@ -12,6 +13,10 @@ type EnabledDisabledInfo struct {
RunOnError bool RunOnError bool
} }
var (
currentVersion = "0.7.0"
)
var ( var (
id atomic.Int64 // default value is 0 id atomic.Int64 // default value is 0
) )
@@ -26,3 +31,20 @@ func NextId() int64 {
func GetFunctionName(i interface{}) string { func GetFunctionName(i interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
} }
// GetEquivalentWebsocketScheme returns the equivalent websocket scheme for the given scheme.
// If the scheme is http or https, it returns ws or wss respectively.
// If the scheme is ws or wss, it returns the same scheme.
// If the scheme is not any of the above, it returns an error.
func GetEquivalentWebsocketScheme(scheme string) (string, error) {
switch scheme {
case "http":
return "ws", nil
case "https":
return "wss", nil
case "ws", "wss":
return scheme, nil
default:
return "", fmt.Errorf("unexpected scheme: %s", scheme)
}
}

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type AdaptiveLighting struct { type AdaptiveLighting struct {
conn *ws.WebsocketWriter conn *connect.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type AlarmControlPanel struct { type AlarmControlPanel struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,14 +1,14 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" "github.com/Xevion/go-ha/internal/connect"
"github.com/Xevion/go-ha/types" "github.com/Xevion/go-ha/types"
) )
/* Structs */ /* Structs */
type Climate struct { type Climate struct {
conn *ws.WebsocketWriter conn *connect.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type Cover struct { type Cover struct {
conn *ws.WebsocketWriter conn *connect.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -2,11 +2,11 @@ package services
import ( import (
"github.com/Xevion/go-ha/internal" "github.com/Xevion/go-ha/internal"
ws "github.com/Xevion/go-ha/internal/websocket" "github.com/Xevion/go-ha/internal/connect"
) )
type Event struct { type Event struct {
conn *ws.WebsocketWriter conn *connect.HAConnection
} }
// Fire an event // Fire an event

View File

@@ -1,11 +1,11 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" "github.com/Xevion/go-ha/internal/connect"
) )
type HomeAssistant struct { type HomeAssistant struct {
conn *ws.WebsocketWriter conn *connect.HAConnection
} }
// TurnOn a Home Assistant entity. Takes an entityId and an optional // TurnOn a Home Assistant entity. Takes an entityId and an optional

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type InputBoolean struct { type InputBoolean struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type InputButton struct { type InputButton struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -4,13 +4,13 @@ import (
"fmt" "fmt"
"time" "time"
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type InputDatetime struct { type InputDatetime struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type InputNumber struct { type InputNumber struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type InputText struct { type InputText struct {
conn *ws.WebsocketWriter conn *connect.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type Light struct { type Light struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type Lock struct { type Lock struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type MediaPlayer struct { type MediaPlayer struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,12 +1,12 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
"github.com/Xevion/go-ha/types" "github.com/Xevion/go-ha/types"
) )
type Notify struct { type Notify struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
// Notify sends a notification. Takes a types.NotifyRequest. // Notify sends a notification. Takes a types.NotifyRequest.

View File

@@ -1,11 +1,11 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
type Number struct { type Number struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
func (ib Number) SetValue(entityId string, value float32) error { func (ib Number) SetValue(entityId string, value float32) error {

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type Scene struct { type Scene struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type Script struct { type Script struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -2,7 +2,7 @@ package services
import ( import (
"github.com/Xevion/go-ha/internal" "github.com/Xevion/go-ha/internal"
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
func BuildService[ func BuildService[
@@ -29,7 +29,7 @@ func BuildService[
Timer | Timer |
Vacuum | Vacuum |
ZWaveJS, ZWaveJS,
](conn *ws.WebsocketWriter) *T { ](conn *ws.HAConnection) *T {
return &T{conn: conn} return &T{conn: conn}
} }

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type Switch struct { type Switch struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type Timer struct { type Timer struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" connect "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type TTS struct { type TTS struct {
conn *ws.WebsocketWriter conn *connect.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type Vacuum struct { type Vacuum struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,13 +1,13 @@
package services package services
import ( import (
ws "github.com/Xevion/go-ha/internal/websocket" ws "github.com/Xevion/go-ha/internal/connect"
) )
/* Structs */ /* Structs */
type ZWaveJS struct { type ZWaveJS struct {
conn *ws.WebsocketWriter conn *ws.HAConnection
} }
/* Public API */ /* Public API */

View File

@@ -1,8 +1,8 @@
package gomeassistant package gomeassistant
import ( import (
"github.com/Xevion/go-ha/internal/connect"
"github.com/Xevion/go-ha/internal/services" "github.com/Xevion/go-ha/internal/services"
ws "github.com/Xevion/go-ha/internal/websocket"
) )
type Service struct { type Service struct {
@@ -31,7 +31,7 @@ type Service struct {
ZWaveJS *services.ZWaveJS ZWaveJS *services.ZWaveJS
} }
func newService(conn *ws.WebsocketWriter) *Service { func newService(conn *connect.HAConnection) *Service {
return &Service{ return &Service{
AdaptiveLighting: services.BuildService[services.AdaptiveLighting](conn), AdaptiveLighting: services.BuildService[services.AdaptiveLighting](conn),
AlarmControlPanel: services.BuildService[services.AlarmControlPanel](conn), AlarmControlPanel: services.BuildService[services.AlarmControlPanel](conn),

View File

@@ -83,7 +83,7 @@ func (s *StateImpl) Get(entityId string) (EntityState, error) {
// ListEntities returns a list of all entities in Home Assistant. // ListEntities returns a list of all entities in Home Assistant.
// see rest documentation for more details: https://developers.home-assistant.io/docs/api/rest/#actions // see rest documentation for more details: https://developers.home-assistant.io/docs/api/rest/#actions
func (s *StateImpl) ListEntities() ([]EntityState, error) { func (s *StateImpl) ListEntities() ([]EntityState, error) {
resp, err := s.httpClient.States() resp, err := s.httpClient.GetStates()
if err != nil { if err != nil {
return nil, err return nil, err
} }