figured out how to get conn/ctx into users method calls - by passing a configured service object to their callback functions

This commit is contained in:
Sam Lewis
2022-10-09 16:28:42 -04:00
parent 889d7ab993
commit bdc5e9295d
11 changed files with 428 additions and 83 deletions

71
app.go
View File

@@ -1,43 +1,50 @@
package main
package gomeassistant
import "time"
import (
"context"
"time"
)
type App struct {
Name string
Schedules []Schedule
Listeners []Listener
type app struct {
url string
ctx context.Context
schedules []Schedule
listeners []Listener
}
type Schedule struct {
/*
RunEvery is a time.Duration representing how often you want to run your function.
Some examples:
time.Second * 5 // runs every 5 seconds at 00:00:00, 00:00:05, etc.
time.Hour * 12 // runs at BaseTime, +12 hours, +24 hours, etc.
gomeassistant.Daily // runs at BaseTime, +24 hours, +48 hours, etc. Daily is a const helper for time.Hour * 24
// Helpers include Daily, Hourly, Minutely
*/
RunEvery time.Duration
/* Callback is the function you want to run. Takes zero arguments. */
Callback func()
/*
BaseTime is 4 character string representing hours and minutes
in a 24-hr format.
It is the base that your RunEvery will be added to.
Defaults to "0000" (which is probably fine for most cases).
Example: Run in the 3rd minute of every hour.
Schedule{
RunEvery: gomeassistant.Hourly // helper const for time.Hour
BaseTime: "0003"
func App(url string) (app, error) {
// TODO: connect to websocket, return error if fails
return app{url: url}, nil
}
*/
BaseTime string
func (a app) RegisterSchedule(s Schedule) {
if s.err != nil {
panic(s.err) // something wasn't configured properly when the schedule was built
}
if s.frequency == 0 {
panic("A schedule must call either Daily() or Every() when built.")
}
now := time.Now()
startTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) // start at midnight today
// apply offset if set
if s.offset.Hour != 0 || s.offset.Minute != 0 {
startTime.Add(time.Hour * time.Duration(s.offset.Hour))
startTime.Add(time.Minute * time.Duration(s.offset.Minute))
}
// advance first scheduled time by frequency until it is in the future
for startTime.Before(now) {
startTime = startTime.Add(s.frequency)
}
// TODO: save realStartTime or _startTime to s, add to list of Schedules
}
const (
RunEveryMissing time.Duration = 0
FrequencyMissing time.Duration = 0
Daily time.Duration = time.Hour * 24
Hourly time.Duration = time.Hour

37
cmd/main/testing.go Normal file
View File

@@ -0,0 +1,37 @@
package main
import (
"fmt"
"time"
ga "github.com/saml-dev/gome-assistant"
)
func main() {
app, err := ga.App("192.168.86.67:8123")
if err != nil {
panic(err)
}
s := ga.ScheduleBuilder().Call(lightsOut).Daily().At(ga.HourMinute(22, 00)).Build()
s2 := ga.ScheduleBuilder().Call(lightsOut).Every(time.Hour * 4).Offset(ga.HourMinute(1, 0)).Build()
app.RegisterSchedule(s2)
// err = app.Start()
simpleListener := ga.EntityListenerBuilder().
EntityId("light.lights").
Call(cool).
OnlyBetween(ga.HourMinute(22, 00), ga.HourMinute(07, 00))
fmt.Println(simpleListener)
// p := ga.NewPersonBuilder().Lives().At("lskdjflskf").WithPostalCode("kdjf").Works().As("SWE")
fmt.Println(s, s2)
}
func lightsOut(service ga.Service) {
// ga.TurnOff("light.all_lights")
}
func cool(service ga.Service, data ga.Data) {
service.Light.TurnOn("light.entryway_lamp")
}

56
entitylistener.go Normal file
View File

@@ -0,0 +1,56 @@
package gomeassistant
type entityListener struct {
entityId string
callback entityListenerCallback
fromState string
toState string
betweenStart hourMinute
betweenEnd hourMinute
}
type entityListenerCallback func(Service, Data)
type Data struct{}
func EntityListenerBuilder() elBuilder1 {
return elBuilder1{entityListener{}}
}
type elBuilder1 struct {
entityListener
}
func (b elBuilder1) EntityId(eid string) elBuilder2 {
b.entityListener.entityId = eid
return elBuilder2(b)
}
type elBuilder2 struct {
entityListener
}
func (b elBuilder2) Call(callback entityListenerCallback) elBuilder3 {
b.entityListener.callback = callback
return elBuilder3(b)
}
type elBuilder3 struct {
entityListener
}
func (b elBuilder3) OnlyBetween(start hourMinute, end hourMinute) elBuilder3 {
b.entityListener.betweenStart = start
b.entityListener.betweenEnd = end
return b
}
func (b elBuilder3) FromState(s string) elBuilder3 {
b.entityListener.fromState = s
return b
}
func (b elBuilder3) ToState(s string) elBuilder3 {
b.entityListener.toState = s
return b
}

View File

@@ -1,4 +1,4 @@
package main
package gomeassistant
import (
"context"
@@ -9,49 +9,14 @@ import (
"nhooyr.io/websocket"
)
var c = context.Background()
var b, x = context.WithTimeout(c, time.Second)
const a = time.April
var ctx, ctxCancel = context.WithTimeout(context.Background(), time.Second*5)
var conn, _, err = websocket.Dial(ctx, "ws://192.168.86.67:8123/api/websocket", nil)
type Light struct {
EntityId string
}
type LightOnRequest 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"`
}
func NewLightOnRequest(entity_id string) LightOnRequest {
req := LightOnRequest{
Id: 5,
Type: "call_service",
Domain: "light",
Service: "turn_on",
}
req.Target.EntityId = entity_id
return req
}
func (l *Light) TurnOn() error {
// req := json.Marshal()
return nil
}
func main() {
sched := Schedule{
RunEvery: Daily,
}
// sched := Schedule{
// RunEvery: Daily,
// }
defer ctxCancel()
if err != nil {
panic(err)

View File

@@ -1,4 +0,0 @@
package light
type Light struct {
}

View File

@@ -1 +0,0 @@
package network

View File

@@ -5,20 +5,16 @@ import (
"encoding/json"
"fmt"
"os"
"time"
"nhooyr.io/websocket"
)
var ctx, ctxCancel = context.WithTimeout(context.Background(), time.Second*5)
var conn, _, err = websocket.Dial(ctx, "ws://192.168.86.67:8123/api/websocket", nil)
type AuthMessage struct {
MsgType string `json:"type"`
AccessToken string `json:"access_token"`
}
func SendAuthMessage() error {
func SendAuthMessage(conn websocket.Conn, ctx context.Context) error {
token := os.Getenv("AUTH_TOKEN")
msgJson, err := json.Marshal(AuthMessage{MsgType: "auth", AccessToken: token})
if err != nil {
@@ -31,7 +27,7 @@ func SendAuthMessage() error {
return nil
}
func WriteMessage[T any](msg T) error {
func WriteMessage[T any](msg T, conn websocket.Conn, ctx context.Context) error {
msgJson, err := json.Marshal(msg)
fmt.Println(string(msgJson))
if err != nil {
@@ -46,7 +42,7 @@ func WriteMessage[T any](msg T) error {
return nil
}
func ReadMessage() (string, error) {
func ReadMessage(conn websocket.Conn, ctx context.Context) (string, error) {
_, msg, err := conn.Read(ctx)
if err != nil {
return "", err

View File

@@ -0,0 +1,50 @@
package services
import (
"context"
"github.com/saml-dev/gome-assistant/internal/network"
"nhooyr.io/websocket"
)
type Light struct {
conn websocket.Conn
ctx context.Context
}
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"`
}
func LightOnRequest(entityId string) LightRequest {
req := LightRequest{
Id: 5,
Type: "call_service",
Domain: "light",
Service: "turn_on",
}
req.Target.EntityId = entityId
return req
}
func LightOffRequest(entityId string) LightRequest {
req := LightOnRequest(entityId)
req.Service = "turn_off"
return req
}
func (l Light) TurnOn(entityId string) {
req := LightOnRequest(entityId)
network.WriteMessage(req, l.conn, l.ctx)
}
func (l Light) TurnOff(entityId string) {
req := LightOffRequest(entityId)
network.WriteMessage(req, l.conn, l.ctx)
}

100
person.go Normal file
View File

@@ -0,0 +1,100 @@
package gomeassistant
import "fmt"
type Person struct {
// Personal details
name, address, pin string
// Job details
workAddress, company, position string
salary int
}
// PersonBuilder struct
type PersonBuilder struct {
person *Person
}
// PersonAddressBuilder facet of PersonBuilder
type PersonAddressBuilder struct {
PersonBuilder
}
// PersonJobBuilder facet of PersonBuilder
type PersonJobBuilder struct {
PersonBuilder
}
// NewPersonBuilder constructor for PersonBuilder
func NewPersonBuilder() *PersonBuilder {
return &PersonBuilder{person: &Person{}}
}
// Lives chains to type *PersonBuilder and returns a *PersonAddressBuilder
func (b *PersonBuilder) Lives() *PersonAddressBuilder {
return &PersonAddressBuilder{*b}
}
// Works chains to type *PersonBuilder and returns a *PersonJobBuilder
func (b *PersonBuilder) Works() *PersonJobBuilder {
return &PersonJobBuilder{*b}
}
// At adds address to person
func (a *PersonAddressBuilder) At(address string) *PersonAddressBuilder {
a.person.address = address
return a
}
// WithPostalCode adds postal code to person
func (a *PersonAddressBuilder) WithPostalCode(pin string) *PersonAddressBuilder {
a.person.pin = pin
return a
}
// As adds position to person
func (j *PersonJobBuilder) As(position string) *PersonJobBuilder {
j.person.position = position
return j
}
// For adds company to person
func (j *PersonJobBuilder) For(company string) *PersonJobBuilder {
j.person.company = company
return j
}
// In adds company address to person
func (j *PersonJobBuilder) In(companyAddress string) *PersonJobBuilder {
j.person.workAddress = companyAddress
return j
}
// WithSalary adds salary to person
func (j *PersonJobBuilder) WithSalary(salary int) *PersonJobBuilder {
j.person.salary = salary
return j
}
// Build builds a person from PersonBuilder
func (b *PersonBuilder) Build() *Person {
return b.person
}
// RunBuilderFacet example
func RunBuilderFacet() {
pb := NewPersonBuilder()
pb.
Lives().
At("Bangalore").
WithPostalCode("560102").
Works().
As("Software Engineer").
For("IBM").
In("Bangalore").
WithSalary(150000)
person := pb.Build()
fmt.Println(person)
}

116
schedule.go Normal file
View File

@@ -0,0 +1,116 @@
package gomeassistant
import (
"fmt"
"reflect"
"runtime"
"time"
)
type hourMinute struct {
Hour int
Minute int
}
func HourMinute(Hour, Minute int) hourMinute {
return hourMinute{Hour, Minute}
}
type scheduleCallback func(Service)
type Schedule struct {
/*
frequency is a time.Duration representing how often you want to run your function.
Some examples:
time.Second * 5 // runs every 5 seconds at 00:00:00, 00:00:05, etc.
time.Hour * 12 // runs at offset, +12 hours, +24 hours, etc.
gomeassistant.Daily // runs at offset, +24 hours, +48 hours, etc. Daily is a const helper for time.Hour * 24
// Helpers include Daily, Hourly, Minutely
*/
frequency time.Duration
callback scheduleCallback
/*
offset is 4 character string representing hours and minutes
in a 24-hr format.
It is the base that your frequency will be added to.
Defaults to "0000" (which is probably fine for most cases).
Example: Run in the 3rd minute of every hour.
Schedule{
frequency: gomeassistant.Hourly // helper const for time.Hour
offset: "0003"
}
*/
offset hourMinute
/*
This will be set rather than returning an error to avoid checking err for nil on every schedule :)
RegisterSchedule will panic if the error is set.
*/
err error
}
type scheduleBuilder struct {
schedule Schedule
}
type scheduleBuilderCall struct {
schedule Schedule
}
type scheduleBuilderDaily struct {
schedule Schedule
}
type scheduleBuilderCustom struct {
schedule Schedule
}
type scheduleBuilderEnd struct {
schedule Schedule
}
func ScheduleBuilder() scheduleBuilder {
return scheduleBuilder{Schedule{}}
}
func (s Schedule) String() string {
return fmt.Sprintf("Run %q every %v with offset %s", getFunctionName(s.callback), s.frequency, s.offset)
}
func (sb scheduleBuilder) Call(callback scheduleCallback) scheduleBuilderCall {
sb.schedule.callback = callback
return scheduleBuilderCall(sb)
}
func (sb scheduleBuilderCall) Daily() scheduleBuilderDaily {
sb.schedule.frequency = time.Hour * 24
return scheduleBuilderDaily(sb)
}
func (sb scheduleBuilderDaily) At(t hourMinute) scheduleBuilderEnd {
sb.schedule.offset = t
return scheduleBuilderEnd(sb)
}
func (sb scheduleBuilderCall) Every(d time.Duration) scheduleBuilderCustom {
sb.schedule.frequency = d
return scheduleBuilderCustom(sb)
}
func (sb scheduleBuilderCustom) Offset(o hourMinute) scheduleBuilderEnd {
sb.schedule.offset = o
return scheduleBuilderEnd(sb)
}
func (sb scheduleBuilderCustom) Build() Schedule {
return sb.schedule
}
func (sb scheduleBuilderEnd) Build() Schedule {
return sb.schedule
}
func getFunctionName(i interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

23
service.go Normal file
View File

@@ -0,0 +1,23 @@
package gomeassistant
import (
"context"
"github.com/saml-dev/gome-assistant/internal/services"
"nhooyr.io/websocket"
)
type Service struct {
HomeAssistant homeAssistant
Light services.Light
}
type homeAssistant struct {
conn websocket.Conn
ctx context.Context
}
// type light struct {
// conn websocket.Conn
// ctx context.Context
// }