From 66e179c5684ba6dbeaf3d3e2f628dfc3ef21cc1f Mon Sep 17 00:00:00 2001 From: Sam Lewis Date: Mon, 24 Oct 2022 23:03:04 -0400 Subject: [PATCH] sunrise sunset working, not the cleanest solution but it works --- app.go | 28 ++++++++++------------------ eventListener.go | 3 --- example/example.go | 13 +++++++++++++ schedule.go | 45 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 61 insertions(+), 28 deletions(-) diff --git a/app.go b/app.go index 449b688..ec56622 100644 --- a/app.go +++ b/app.go @@ -36,7 +36,7 @@ type TimeString string NewApp establishes the websocket connection and returns an object you can use to register schedules and listeners. */ -func NewApp(connString string) app { +func NewApp(connString string) *app { token := os.Getenv("HA_AUTH_TOKEN") conn, ctx, ctxCancel := ws.SetupConnection(connString, token) @@ -45,7 +45,7 @@ func NewApp(connString string) app { service := NewService(conn, ctx, httpClient) state := newState(httpClient) - return app{ + return &app{ conn: conn, ctx: ctx, ctxCancel: ctxCancel, @@ -65,6 +65,12 @@ func (a *app) Cleanup() { } func (a *app) RegisterSchedule(s schedule) { + // realStartTime already set for sunset/sunrise + if s.isSunrise || s.isSunset { + a.schedules.Insert(s, float64(s.realStartTime.Unix())) + return + } + if s.frequency == 0 { log.Fatalln("A schedule must call either Daily() or Every() when built.") } @@ -106,21 +112,7 @@ func (a *app) RegisterEventListener(evl eventListener) { } } -// Sunrise take an optional string that is passed to time.ParseDuration. -// Examples include "-1.5h", "30m", etc. See https://pkg.go.dev/time#ParseDuration -// for full list. -func (a *app) Sunrise(offset ...TimeString) string { - return getSunriseSunset(a, true, offset) -} - -// Sunset take an optional string that is passed to time.ParseDuration. -// Examples include "-1.5h", "30m", etc. See https://pkg.go.dev/time#ParseDuration -// for full list. -func (a *app) Sunset(offset ...TimeString) string { - return getSunriseSunset(a, false, offset) -} - -func getSunriseSunset(a *app, sunrise bool, offset []TimeString) string { +func getSunriseSunset(a *app, sunrise bool, offset []TimeString) carbon.Carbon { printString := "Sunset" attrKey := "next_setting" if sunrise { @@ -150,7 +142,7 @@ func getSunriseSunset(a *app, sunrise bool, offset []TimeString) string { nextSetOrRise = nextSetOrRise.AddMinutes(int(t.Minutes())) } - return carbon2TimeString(nextSetOrRise) + return nextSetOrRise } func carbon2TimeString(c carbon.Carbon) string { diff --git a/eventListener.go b/eventListener.go index a3ce691..deaeb42 100644 --- a/eventListener.go +++ b/eventListener.go @@ -1,8 +1,5 @@ package gomeassistant -// TODO: impl eventListener. could probably create generic listener struct for -// code reuse between eventListener and eventListener - import ( "encoding/json" "log" diff --git a/example/example.go b/example/example.go index 13f05b0..7b207d2 100644 --- a/example/example.go +++ b/example/example.go @@ -25,6 +25,13 @@ func main() { At("23:00"). Build() + _30minsBeforeSunrise := ga. + ScheduleBuilder(). + Call(sunriseSched). + Daily(). + Sunrise(app, "-30m"). + Build() + zwaveEventListener := ga. EventListenerBuilder(). EventTypes("zwave_js_value_notification"). @@ -33,6 +40,7 @@ func main() { app.RegisterEntityListener(pantryDoor) app.RegisterSchedule(_11pmSched) + app.RegisterSchedule(_30minsBeforeSunrise) app.RegisterEventListener(zwaveEventListener) app.Start() @@ -72,3 +80,8 @@ func lightsOut(service *ga.Service, state *ga.State) { service.Light.TurnOff("light.main_lights") } } + +func sunriseSched(service *ga.Service, state *ga.State) { + service.Light.TurnOn("light.living_room_lamps") + service.Light.TurnOff("light.christmas_lights") +} diff --git a/schedule.go b/schedule.go index ef6c319..aded6b4 100644 --- a/schedule.go +++ b/schedule.go @@ -33,6 +33,10 @@ type schedule struct { */ offset time.Duration realStartTime time.Time + + isSunrise bool + isSunset bool + sunOffset TimeString } func (s schedule) Hash() string { @@ -107,6 +111,24 @@ func (sb scheduleBuilderDaily) At(s string) scheduleBuilderEnd { return scheduleBuilderEnd(sb) } +// Sunrise takes an app pointer and an optional duration string that is passed to time.ParseDuration. +// Examples include "-1.5h", "30m", etc. See https://pkg.go.dev/time#ParseDuration +// for full list. +func (sb scheduleBuilderDaily) Sunrise(a *app, offset ...TimeString) scheduleBuilderEnd { + sb.schedule.realStartTime = getSunriseSunset(a, true, offset).Carbon2Time() + sb.schedule.isSunrise = true + return scheduleBuilderEnd(sb) +} + +// Sunset takes an app pointer and an optional duration string that is passed to time.ParseDuration. +// Examples include "-1.5h", "30m", etc. See https://pkg.go.dev/time#ParseDuration +// for full list. +func (sb scheduleBuilderDaily) Sunset(a *app, offset ...TimeString) scheduleBuilderEnd { + sb.schedule.realStartTime = getSunriseSunset(a, false, offset).Carbon2Time() + sb.schedule.isSunset = true + return scheduleBuilderEnd(sb) +} + func (sb scheduleBuilderCall) Every(s TimeString) scheduleBuilderCustom { d, err := time.ParseDuration(string(s)) if err != nil { @@ -145,7 +167,7 @@ func runSchedules(a *app) { for { sched := popSchedule(a) - // log.Default().Println(sched.realStartTime) + log.Default().Println(sched.realStartTime) // run callback for all schedules before now in case they overlap for sched.realStartTime.Before(time.Now()) { @@ -167,11 +189,20 @@ func popSchedule(a *app) schedule { } func requeueSchedule(a *app, s schedule) { - // TODO: figure out how to handle sunset/sunrise in here. Maybe just - // add sunrise bool and sunset bool to Schedule, might have to change - // API to be .Call().Sunset("1h") instead of .Call().At(ga.Sunset("1h")) - // then that function could easily set the flag. Kinda ruins the english - // language sentence structure but maybe simplest way to get it working - s.realStartTime = s.realStartTime.Add(s.frequency) + if s.isSunrise || s.isSunset { + nextSunTime := getSunriseSunset(a, s.isSunrise, []TimeString{s.sunOffset}) + + // this is true when there is a negative offset, so schedule runs before sunset/sunrise and + // HA still shows today's sunset as next sunset. Just add 24h as a default handler + // since we can't get tomorrow's sunset from HA at this point. + if nextSunTime.IsToday() { + nextSunTime = nextSunTime.AddHours(24) + } + + s.realStartTime = nextSunTime.Carbon2Time() + } else { + s.realStartTime = s.realStartTime.Add(s.frequency) + } + a.schedules.Insert(s, float64(s.realStartTime.Unix())) }