mirror of
https://github.com/Xevion/go-ha.git
synced 2026-01-31 06:24:27 -06:00
fix bug when registering multiple listeners and change sunrise/sunset to non-ha impl
This commit is contained in:
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/golang-module/carbon"
|
||||
"github.com/gorilla/websocket"
|
||||
sunriseLib "github.com/nathan-osman/go-sunrise"
|
||||
"saml.dev/gome-assistant/internal"
|
||||
"saml.dev/gome-assistant/internal/http"
|
||||
pq "saml.dev/gome-assistant/internal/priorityqueue"
|
||||
@@ -26,9 +26,9 @@ type App struct {
|
||||
|
||||
schedules pq.PriorityQueue
|
||||
intervals pq.PriorityQueue
|
||||
entityListeners map[string][]*EntityListener
|
||||
entityListeners map[string][]EntityListener
|
||||
entityListenersId int64
|
||||
eventListeners map[string][]*EventListener
|
||||
eventListeners map[string][]EventListener
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -47,18 +47,46 @@ type timeRange struct {
|
||||
end time.Time
|
||||
}
|
||||
|
||||
type NewAppRequest struct {
|
||||
// Required
|
||||
// IpAddress of your Home Assistant instance i.e. "localhost"
|
||||
// or "192.168.86.59" etc.
|
||||
IpAddress string
|
||||
|
||||
// Optional
|
||||
// Port number Home Assistant is running on. Defaults to 8123.
|
||||
Port string
|
||||
|
||||
// Required
|
||||
// Auth token generated in Home Assistant. Used
|
||||
// to connect to the Websocket API.
|
||||
HAAuthToken string
|
||||
|
||||
// Required
|
||||
// EntityId of the zone representing your home e.g. "zone.home".
|
||||
// Used to pull latitude/longitude from Home Assistant
|
||||
// to calculate sunset/sunrise times.
|
||||
HomeZoneEntityId string
|
||||
}
|
||||
|
||||
/*
|
||||
NewApp establishes the websocket connection and returns an object
|
||||
you can use to register schedules and listeners.
|
||||
*/
|
||||
func NewApp(connString string) *App {
|
||||
token := os.Getenv("HA_AUTH_TOKEN")
|
||||
conn, ctx, ctxCancel := ws.SetupConnection(connString, token)
|
||||
func NewApp(request NewAppRequest) *App {
|
||||
if request.IpAddress == "" || request.HAAuthToken == "" || request.HomeZoneEntityId == "" {
|
||||
log.Fatalln("IpAddress, HAAuthToken, and HomeZoneEntityId are all required arguments in NewAppRequest.")
|
||||
}
|
||||
port := request.Port
|
||||
if port == "" {
|
||||
port = "8123"
|
||||
}
|
||||
conn, ctx, ctxCancel := ws.SetupConnection(request.IpAddress, port, request.HAAuthToken)
|
||||
|
||||
httpClient := http.NewHttpClient(connString, token)
|
||||
httpClient := http.NewHttpClient(request.IpAddress, port, request.HAAuthToken)
|
||||
|
||||
service := newService(conn, ctx, httpClient)
|
||||
state := newState(httpClient)
|
||||
state := newState(httpClient, request.HomeZoneEntityId)
|
||||
|
||||
return &App{
|
||||
conn: conn,
|
||||
@@ -69,8 +97,8 @@ func NewApp(connString string) *App {
|
||||
state: state,
|
||||
schedules: pq.New(),
|
||||
intervals: pq.New(),
|
||||
entityListeners: map[string][]*EntityListener{},
|
||||
eventListeners: map[string][]*EventListener{},
|
||||
entityListeners: map[string][]EntityListener{},
|
||||
eventListeners: map[string][]EventListener{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +112,7 @@ func (a *App) RegisterSchedules(schedules ...DailySchedule) {
|
||||
for _, s := range schedules {
|
||||
// realStartTime already set for sunset/sunrise
|
||||
if s.isSunrise || s.isSunset {
|
||||
s.nextRunTime = getSunriseSunsetFromApp(a, s.isSunrise, s.sunOffset).Carbon2Time()
|
||||
s.nextRunTime = getNextSunRiseOrSet(a, s.isSunrise, s.sunOffset).Carbon2Time()
|
||||
a.schedules.Insert(s, float64(s.nextRunTime.Unix()))
|
||||
continue
|
||||
}
|
||||
@@ -105,7 +133,7 @@ func (a *App) RegisterSchedules(schedules ...DailySchedule) {
|
||||
func (a *App) RegisterIntervals(intervals ...Interval) {
|
||||
for _, i := range intervals {
|
||||
if i.frequency == 0 {
|
||||
panic("A schedule must use either set frequency via Every().")
|
||||
log.Fatalf("A schedule must use either set frequency via Every().\n")
|
||||
}
|
||||
|
||||
i.nextRunTime = internal.ParseTime(string(i.startTime)).Carbon2Time()
|
||||
@@ -113,7 +141,6 @@ func (a *App) RegisterIntervals(intervals ...Interval) {
|
||||
for i.nextRunTime.Before(now) {
|
||||
i.nextRunTime = i.nextRunTime.Add(i.frequency)
|
||||
}
|
||||
fmt.Println(i)
|
||||
a.intervals.Insert(i, float64(i.nextRunTime.Unix()))
|
||||
}
|
||||
}
|
||||
@@ -121,14 +148,14 @@ func (a *App) RegisterIntervals(intervals ...Interval) {
|
||||
func (a *App) RegisterEntityListeners(etls ...EntityListener) {
|
||||
for _, etl := range etls {
|
||||
if etl.delay != 0 && etl.toState == "" {
|
||||
panic("EntityListener error: you have to use ToState() when using Duration()")
|
||||
log.Fatalln("EntityListener error: you have to use ToState() when using Duration()")
|
||||
}
|
||||
|
||||
for _, entity := range etl.entityIds {
|
||||
if elList, ok := a.entityListeners[entity]; ok {
|
||||
a.entityListeners[entity] = append(elList, &etl)
|
||||
a.entityListeners[entity] = append(elList, etl)
|
||||
} else {
|
||||
a.entityListeners[entity] = []*EntityListener{&etl}
|
||||
a.entityListeners[entity] = []EntityListener{etl}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,50 +165,54 @@ func (a *App) RegisterEventListeners(evls ...EventListener) {
|
||||
for _, evl := range evls {
|
||||
for _, eventType := range evl.eventTypes {
|
||||
if elList, ok := a.eventListeners[eventType]; ok {
|
||||
a.eventListeners[eventType] = append(elList, &evl)
|
||||
a.eventListeners[eventType] = append(elList, evl)
|
||||
} else {
|
||||
ws.SubscribeToEventType(eventType, a.conn, a.ctx)
|
||||
a.eventListeners[eventType] = []*EventListener{&evl}
|
||||
a.eventListeners[eventType] = []EventListener{evl}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getSunriseSunsetFromState(s *State, sunrise bool, offset ...DurationString) carbon.Carbon {
|
||||
func getSunriseSunset(s *State, sunrise bool, dateToUse carbon.Carbon, offset ...DurationString) carbon.Carbon {
|
||||
date := dateToUse.Carbon2Time()
|
||||
rise, set := sunriseLib.SunriseSunset(s.latitude, s.longitude, date.Year(), date.Month(), date.Day())
|
||||
rise, set = rise.Local(), set.Local()
|
||||
|
||||
val := set
|
||||
printString := "Sunset"
|
||||
attrKey := "next_setting"
|
||||
if sunrise {
|
||||
val = rise
|
||||
printString = "Sunrise"
|
||||
attrKey = "next_rising"
|
||||
}
|
||||
|
||||
setOrRiseToday := carbon.Parse(val.String())
|
||||
|
||||
var t time.Duration
|
||||
var err error
|
||||
if len(offset) == 1 {
|
||||
t, err = time.ParseDuration(string(offset[0]))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Could not parse offset passed to %s: \"%s\"", printString, offset[0]))
|
||||
log.Fatalf(fmt.Sprintf("Could not parse offset passed to %s: \"%s\"\n", printString, offset[0]))
|
||||
}
|
||||
}
|
||||
|
||||
// get next sunrise/sunset time from HA
|
||||
state, err := s.Get("sun.sun")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Couldn't get sun.sun state from HA to calculate %s", printString))
|
||||
}
|
||||
|
||||
nextSetOrRise := carbon.Parse(state.Attributes[attrKey].(string))
|
||||
|
||||
// add offset if set, this code works for negative values too
|
||||
if t.Microseconds() != 0 {
|
||||
nextSetOrRise = nextSetOrRise.AddMinutes(int(t.Minutes()))
|
||||
setOrRiseToday = setOrRiseToday.AddMinutes(int(t.Minutes()))
|
||||
}
|
||||
|
||||
return nextSetOrRise
|
||||
return setOrRiseToday
|
||||
}
|
||||
|
||||
func getSunriseSunsetFromApp(a *App, sunrise bool, offset ...DurationString) carbon.Carbon {
|
||||
return getSunriseSunsetFromState(a.state, sunrise, offset...)
|
||||
func getNextSunRiseOrSet(a *App, sunrise bool, offset ...DurationString) carbon.Carbon {
|
||||
sunriseOrSunset := getSunriseSunset(a.state, sunrise, carbon.Now(), offset...)
|
||||
if sunriseOrSunset.Lt(carbon.Now()) {
|
||||
// if we're past today's sunset or sunrise (accounting for offset) then get tomorrows
|
||||
// as that's the next time the schedule will run
|
||||
sunriseOrSunset = getSunriseSunset(a.state, sunrise, carbon.Tomorrow(), offset...)
|
||||
}
|
||||
return sunriseOrSunset
|
||||
}
|
||||
|
||||
func (a *App) Start() {
|
||||
|
||||
Reference in New Issue
Block a user