diff --git a/app.go b/app.go index 53706a9..ec0c5ed 100644 --- a/app.go +++ b/app.go @@ -56,6 +56,7 @@ func (a *app) Cleanup() { } func (a *app) RegisterSchedule(s schedule) { + s.callback(a.service, a.state) if s.err != nil { log.Fatalln(s.err) // something wasn't configured properly when the schedule was built } diff --git a/cmd/main/testing.go b/cmd/main/testing.go index b6bd420..94cb97d 100644 --- a/cmd/main/testing.go +++ b/cmd/main/testing.go @@ -2,6 +2,7 @@ package main import ( "log" + "time" ga "github.com/saml-dev/gome-assistant" ) @@ -25,7 +26,10 @@ func main() { } func lightsOut(service *ga.Service, state *ga.State) { - service.HomeAssistant.Toggle("light.office_ceiling_lights") + service.InputDatetime.Set("input_datetime.garage_last_triggered_ts", time.Now()) + // service.HomeAssistant.Toggle("group.living_room_lamps", map[string]any{"brightness_pct": 100}) + // service.Light.Toggle("light.entryway_lamp", map[string]any{"brightness_pct": 100}) + // service.Switch.Toggle("switch.espurna_sunroom_lamp") } func cool(service ga.Service, data ga.Data) { diff --git a/internal/services/homeassistant.go b/internal/services/homeassistant.go index 16d438b..7449cb6 100644 --- a/internal/services/homeassistant.go +++ b/internal/services/homeassistant.go @@ -3,21 +3,37 @@ package services import ( "context" - "github.com/saml-dev/gome-assistant/internal/http" ws "github.com/saml-dev/gome-assistant/internal/websocket" "nhooyr.io/websocket" ) type HomeAssistant struct { - conn *websocket.Conn - ctx context.Context - httpClient *http.HttpClient + conn *websocket.Conn + ctx context.Context } -func (ha *HomeAssistant) TurnOn(entityId string) { +// TurnOn a Home Assistant entity. Takes an entityId and an optional +// map that is translated into service_data. +func (ha *HomeAssistant) TurnOn(entityId string, serviceData ...map[string]any) { req := NewBaseServiceRequest(entityId) req.Domain = "homeassistant" req.Service = "turn_on" + if len(serviceData) != 0 { + req.ServiceData = serviceData[0] + } + + ws.WriteMessage(req, ha.conn, ha.ctx) +} + +// Toggle a Home Assistant entity. Takes an entityId and an optional +// map that is translated into service_data. +func (ha *HomeAssistant) Toggle(entityId string, serviceData ...map[string]any) { + req := NewBaseServiceRequest(entityId) + req.Domain = "homeassistant" + req.Service = "toggle" + if len(serviceData) != 0 { + req.ServiceData = serviceData[0] + } ws.WriteMessage(req, ha.conn, ha.ctx) } @@ -29,11 +45,3 @@ func (ha *HomeAssistant) TurnOff(entityId string) { ws.WriteMessage(req, ha.conn, ha.ctx) } - -func (ha *HomeAssistant) Toggle(entityId string) { - req := NewBaseServiceRequest(entityId) - req.Domain = "homeassistant" - req.Service = "toggle" - - ws.WriteMessage(req, ha.conn, ha.ctx) -} diff --git a/internal/services/input_boolean.go b/internal/services/input_boolean.go new file mode 100644 index 0000000..0582fbe --- /dev/null +++ b/internal/services/input_boolean.go @@ -0,0 +1,47 @@ +package services + +import ( + "context" + + ws "github.com/saml-dev/gome-assistant/internal/websocket" + "nhooyr.io/websocket" +) + +/* Structs */ + +type InputBoolean struct { + conn *websocket.Conn + ctx context.Context +} + +/* Public API */ + +func (ib InputBoolean) TurnOn(entityId string) { + req := NewBaseServiceRequest(entityId) + req.Domain = "input_boolean" + req.Service = "turn_on" + + ws.WriteMessage(req, ib.conn, ib.ctx) +} + +func (ib InputBoolean) Toggle(entityId string) { + req := NewBaseServiceRequest(entityId) + req.Domain = "input_boolean" + req.Service = "toggle" + + ws.WriteMessage(req, ib.conn, ib.ctx) +} + +func (ib InputBoolean) TurnOff(entityId string) { + req := NewBaseServiceRequest(entityId) + req.Domain = "input_boolean" + req.Service = "turn_off" + ws.WriteMessage(req, ib.conn, ib.ctx) +} + +func (ib InputBoolean) Reload() { + req := NewBaseServiceRequest("") + req.Domain = "input_boolean" + req.Service = "reload" + ws.WriteMessage(req, ib.conn, ib.ctx) +} diff --git a/internal/services/input_button.go b/internal/services/input_button.go new file mode 100644 index 0000000..f7624d4 --- /dev/null +++ b/internal/services/input_button.go @@ -0,0 +1,32 @@ +package services + +import ( + "context" + + ws "github.com/saml-dev/gome-assistant/internal/websocket" + "nhooyr.io/websocket" +) + +/* Structs */ + +type InputButton struct { + conn *websocket.Conn + ctx context.Context +} + +/* Public API */ + +func (ib InputButton) Press(entityId string) { + req := NewBaseServiceRequest(entityId) + req.Domain = "input_button" + req.Service = "press" + + ws.WriteMessage(req, ib.conn, ib.ctx) +} + +func (ib InputButton) Reload() { + req := NewBaseServiceRequest("") + req.Domain = "input_button" + req.Service = "reload" + ws.WriteMessage(req, ib.conn, ib.ctx) +} diff --git a/internal/services/input_datetime.go b/internal/services/input_datetime.go new file mode 100644 index 0000000..9894ba2 --- /dev/null +++ b/internal/services/input_datetime.go @@ -0,0 +1,38 @@ +package services + +import ( + "context" + "fmt" + "time" + + ws "github.com/saml-dev/gome-assistant/internal/websocket" + "nhooyr.io/websocket" +) + +/* Structs */ + +type InputDatetime struct { + conn *websocket.Conn + ctx context.Context +} + +/* Public API */ + +func (ib InputDatetime) Set(entityId string, value time.Time) { + req := NewBaseServiceRequest(entityId) + req.Domain = "input_datetime" + req.Service = "set_datetime" + req.ServiceData = map[string]any{ + "timestamp": fmt.Sprint(value.Unix()), + } + fmt.Println(req) + + ws.WriteMessage(req, ib.conn, ib.ctx) // TODO: this ain't working for some reason +} + +func (ib InputDatetime) Reload() { + req := NewBaseServiceRequest("") + req.Domain = "input_datetime" + req.Service = "reload" + ws.WriteMessage(req, ib.conn, ib.ctx) +} diff --git a/internal/services/input_number.go b/internal/services/input_number.go new file mode 100644 index 0000000..d89b11e --- /dev/null +++ b/internal/services/input_number.go @@ -0,0 +1,49 @@ +package services + +import ( + "context" + + ws "github.com/saml-dev/gome-assistant/internal/websocket" + "nhooyr.io/websocket" +) + +/* Structs */ + +type InputNumber struct { + conn *websocket.Conn + ctx context.Context +} + +/* Public API */ + +func (ib InputNumber) Set(entityId string, value float32) { + req := NewBaseServiceRequest(entityId) + req.Domain = "input_number" + req.Service = "set_value" + req.ServiceData = map[string]any{"value": value} + + ws.WriteMessage(req, ib.conn, ib.ctx) +} + +func (ib InputNumber) Increment(entityId string) { + req := NewBaseServiceRequest(entityId) + req.Domain = "input_number" + req.Service = "increment" + + ws.WriteMessage(req, ib.conn, ib.ctx) +} + +func (ib InputNumber) Decrement(entityId string) { + req := NewBaseServiceRequest(entityId) + req.Domain = "input_number" + req.Service = "decrement" + + ws.WriteMessage(req, ib.conn, ib.ctx) +} + +func (ib InputNumber) Reload() { + req := NewBaseServiceRequest("") + req.Domain = "input_number" + req.Service = "reload" + ws.WriteMessage(req, ib.conn, ib.ctx) +} diff --git a/internal/services/input_text.go b/internal/services/input_text.go new file mode 100644 index 0000000..0cdec0a --- /dev/null +++ b/internal/services/input_text.go @@ -0,0 +1,35 @@ +package services + +import ( + "context" + + ws "github.com/saml-dev/gome-assistant/internal/websocket" + "nhooyr.io/websocket" +) + +/* Structs */ + +type InputText struct { + conn *websocket.Conn + ctx context.Context +} + +/* Public API */ + +func (ib InputText) Set(entityId string, value string) { + req := NewBaseServiceRequest(entityId) + req.Domain = "input_text" + req.Service = "set_value" + req.ServiceData = map[string]any{ + "value": value, + } + + ws.WriteMessage(req, ib.conn, ib.ctx) +} + +func (ib InputText) Reload() { + req := NewBaseServiceRequest("") + req.Domain = "input_text" + req.Service = "reload" + ws.WriteMessage(req, ib.conn, ib.ctx) +} diff --git a/internal/services/light.go b/internal/services/light.go index 2eac12e..ab59241 100644 --- a/internal/services/light.go +++ b/internal/services/light.go @@ -3,54 +3,48 @@ package services import ( "context" - "github.com/saml-dev/gome-assistant/internal/http" ws "github.com/saml-dev/gome-assistant/internal/websocket" "nhooyr.io/websocket" ) -type Light struct { - conn *websocket.Conn - ctx context.Context - httpClient *http.HttpClient -} +/* Structs */ -type LightRequest struct { - Id int `json:"id"` - Type string `json:"type"` - Domain string `json:"domain"` - Service string `json:"service"` - Target struct { - EntityId string `json:"entity_id"` - } `json:"target"` +type Light struct { + conn *websocket.Conn + ctx context.Context } /* Public API */ -func (l Light) TurnOn(entityId string) { - req := newLightOnRequest(entityId) +// TurnOn a light entity. Takes an entityId and an optional +// map that is translated into service_data. +func (l Light) TurnOn(entityId string, serviceData ...map[string]any) { + req := NewBaseServiceRequest(entityId) + req.Domain = "light" + req.Service = "turn_on" + if len(serviceData) != 0 { + req.ServiceData = serviceData[0] + } + + ws.WriteMessage(req, l.conn, l.ctx) +} + +// Toggle a light entity. Takes an entityId and an optional +// map that is translated into service_data. +func (l Light) Toggle(entityId string, serviceData ...map[string]any) { + req := NewBaseServiceRequest(entityId) + req.Domain = "light" + req.Service = "toggle" + if len(serviceData) != 0 { + req.ServiceData = serviceData[0] + } + ws.WriteMessage(req, l.conn, l.ctx) } func (l Light) TurnOff(entityId string) { - req := newLightOffRequest(entityId) + req := NewBaseServiceRequest(entityId) + req.Domain = "light" + req.Service = "turn_off" ws.WriteMessage(req, l.conn, l.ctx) } - -/* Internal */ - -func newLightOnRequest(entityId string) LightRequest { - req := LightRequest{ - Id: 5, - Type: "call_service", - Domain: "light", - Service: "turn_on", - } - req.Target.EntityId = entityId - return req -} - -func newLightOffRequest(entityId string) LightRequest { - req := newLightOnRequest(entityId) - req.Service = "turn_off" - return req -} diff --git a/internal/services/lock.go b/internal/services/lock.go new file mode 100644 index 0000000..62fe874 --- /dev/null +++ b/internal/services/lock.go @@ -0,0 +1,43 @@ +package services + +import ( + "context" + + ws "github.com/saml-dev/gome-assistant/internal/websocket" + "nhooyr.io/websocket" +) + +/* Structs */ + +type Lock struct { + conn *websocket.Conn + ctx context.Context +} + +/* Public API */ + +// Lock a lock entity. Takes an entityId and an optional +// map that is translated into service_data. +func (l Lock) Lock(entityId string, serviceData ...map[string]any) { + req := NewBaseServiceRequest(entityId) + req.Domain = "lock" + req.Service = "lock" + if len(serviceData) != 0 { + req.ServiceData = serviceData[0] + } + + ws.WriteMessage(req, l.conn, l.ctx) +} + +// Unlock a lock entity. Takes an entityId and an optional +// map that is translated into service_data. +func (l Lock) Unlock(entityId string, serviceData ...map[string]any) { + req := NewBaseServiceRequest(entityId) + req.Domain = "lock" + req.Service = "unlock" + if len(serviceData) != 0 { + req.ServiceData = serviceData[0] + } + + ws.WriteMessage(req, l.conn, l.ctx) +} diff --git a/internal/services/builder.go b/internal/services/services.go similarity index 55% rename from internal/services/builder.go rename to internal/services/services.go index bceb4e4..b639f7e 100644 --- a/internal/services/builder.go +++ b/internal/services/services.go @@ -2,31 +2,46 @@ package services import ( "context" + "fmt" - "github.com/saml-dev/gome-assistant/internal/http" "nhooyr.io/websocket" ) -func BuildService[T Light | HomeAssistant](conn *websocket.Conn, ctx context.Context, httpClient *http.HttpClient) *T { - return &T{conn: conn, ctx: ctx, httpClient: httpClient} +func BuildService[ + T Light | + HomeAssistant | + Lock | + Switch | + InputBoolean | + InputButton | + InputDatetime | + InputText | + InputNumber, +](conn *websocket.Conn, ctx context.Context) *T { + return &T{conn: conn, ctx: ctx} } type BaseServiceRequest struct { - Id int `json:"id"` + Id string `json:"id"` RequestType string `json:"type"` // hardcoded "call_service" Domain string `json:"domain"` Service string `json:"service"` ServiceData map[string]any `json:"service_data,omitempty"` Target struct { EntityId string `json:"entity_id"` - } `json:"target"` + } `json:"target,omitempty"` } +var id int64 = 1 + func NewBaseServiceRequest(entityId string) BaseServiceRequest { bsr := BaseServiceRequest{ - Id: 10, + Id: fmt.Sprint(id), RequestType: "call_service", } - bsr.Target.EntityId = entityId + if entityId != "" { + bsr.Target.EntityId = entityId + } + id += 1 return bsr } diff --git a/internal/services/switch.go b/internal/services/switch.go new file mode 100644 index 0000000..78dfb46 --- /dev/null +++ b/internal/services/switch.go @@ -0,0 +1,40 @@ +package services + +import ( + "context" + + ws "github.com/saml-dev/gome-assistant/internal/websocket" + "nhooyr.io/websocket" +) + +/* Structs */ + +type Switch struct { + conn *websocket.Conn + ctx context.Context +} + +/* Public API */ + +func (s Switch) TurnOn(entityId string) { + req := NewBaseServiceRequest(entityId) + req.Domain = "switch" + req.Service = "turn_on" + + ws.WriteMessage(req, s.conn, s.ctx) +} + +func (s Switch) Toggle(entityId string) { + req := NewBaseServiceRequest(entityId) + req.Domain = "switch" + req.Service = "toggle" + + ws.WriteMessage(req, s.conn, s.ctx) +} + +func (s Switch) TurnOff(entityId string) { + req := NewBaseServiceRequest(entityId) + req.Domain = "switch" + req.Service = "turn_off" + ws.WriteMessage(req, s.conn, s.ctx) +} diff --git a/service.go b/service.go index e9122b2..339ab1e 100644 --- a/service.go +++ b/service.go @@ -11,11 +11,25 @@ import ( type Service struct { HomeAssistant *services.HomeAssistant Light *services.Light + Lock *services.Lock + Switch *services.Switch + InputBoolean *services.InputBoolean + InputButton *services.InputButton + InputText *services.InputText + InputDatetime *services.InputDatetime + InputNumber *services.InputNumber } func NewService(conn *websocket.Conn, ctx context.Context, httpClient *http.HttpClient) *Service { return &Service{ - Light: services.BuildService[services.Light](conn, ctx, httpClient), - HomeAssistant: services.BuildService[services.HomeAssistant](conn, ctx, httpClient), + Light: services.BuildService[services.Light](conn, ctx), + HomeAssistant: services.BuildService[services.HomeAssistant](conn, ctx), + Lock: services.BuildService[services.Lock](conn, ctx), + Switch: services.BuildService[services.Switch](conn, ctx), + InputBoolean: services.BuildService[services.InputBoolean](conn, ctx), + InputButton: services.BuildService[services.InputButton](conn, ctx), + InputText: services.BuildService[services.InputText](conn, ctx), + InputDatetime: services.BuildService[services.InputDatetime](conn, ctx), + InputNumber: services.BuildService[services.InputNumber](conn, ctx), } }