From 21358b73e1ce74cb15da341872a1a04c42e2553d Mon Sep 17 00:00:00 2001 From: Xevion Date: Fri, 1 Aug 2025 18:08:01 -0500 Subject: [PATCH] refactor: move types out of app.go into types/, renamed module files --- app.go | 57 +++----------------------- checkers.go | 7 ++-- checkers_test.go | 9 ++-- cmd/generate/main.go | 3 +- entity_listener.go | 9 ++-- event_listener.go | 7 ++-- internal/websocket/websocket.go | 3 +- interval.go | 17 ++++---- schedule.go | 7 ++-- state.go | 17 ++++---- types/app.go | 35 ++++++++++++++++ types/common.go | 22 ++++++++++ types/{request_types.go => request.go} | 0 13 files changed, 105 insertions(+), 88 deletions(-) create mode 100644 types/app.go create mode 100644 types/common.go rename types/{request_types.go => request.go} (100%) diff --git a/app.go b/app.go index 23d56d2..17f7525 100644 --- a/app.go +++ b/app.go @@ -17,6 +17,7 @@ import ( internal "github.com/Xevion/go-ha/internal" "github.com/Xevion/go-ha/internal/parse" ws "github.com/Xevion/go-ha/internal/websocket" + "github.com/Xevion/go-ha/types" ) var ErrInvalidArgs = errors.New("invalid arguments provided") @@ -41,10 +42,7 @@ type App struct { eventListeners map[string][]*EventListener } -type Item struct { - Value interface{} - Priority float64 -} +type Item types.Item func (mi Item) Compare(other queue.Item) int { if mi.Priority > other.(Item).Priority { @@ -55,51 +53,6 @@ func (mi Item) Compare(other queue.Item) int { return -1 } -// DurationString represents a duration, such as "2s" or "24h". -// See https://pkg.go.dev/time#ParseDuration for all valid time units. -type DurationString string - -// TimeString is a 24-hr format time "HH:MM" such as "07:30". -type TimeString string - -type timeRange struct { - start time.Time - end time.Time -} - -type NewAppRequest struct { - // Required - URL string - - // Optional - // Deprecated: use URL instead - // IpAddress of your Home Assistant instance i.e. "localhost" - // or "192.168.86.59" etc. - IpAddress string - - // Optional - // Deprecated: use URL instead - // 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 - - // Optional - // Whether to use secure connections for http and websockets. - // Setting this to `true` will use `https://` instead of `https://` - // and `wss://` instead of `ws://`. - Secure bool -} - // validateHomeZone verifies that the home zone entity exists and has latitude/longitude func validateHomeZone(state State, entityID string) error { entity, err := state.Get(entityID) @@ -130,7 +83,7 @@ func validateHomeZone(state State, entityID string) error { NewApp establishes the websocket connection and returns an object you can use to register schedules and listeners. */ -func NewApp(request NewAppRequest) (*App, error) { +func NewApp(request types.NewAppRequest) (*App, error) { if (request.URL == "" && request.IpAddress == "") || request.HAAuthToken == "" { slog.Error("URL and HAAuthToken are required arguments in NewAppRequest") return nil, ErrInvalidArgs @@ -306,7 +259,7 @@ func (a *App) RegisterEventListeners(evls ...EventListener) { } } -func getSunriseSunset(s *StateImpl, sunrise bool, dateToUse carbon.Carbon, offset ...DurationString) carbon.Carbon { +func getSunriseSunset(s *StateImpl, sunrise bool, dateToUse carbon.Carbon, offset ...types.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() @@ -339,7 +292,7 @@ func getSunriseSunset(s *StateImpl, sunrise bool, dateToUse carbon.Carbon, offse return setOrRiseToday } -func getNextSunRiseOrSet(a *App, sunrise bool, offset ...DurationString) carbon.Carbon { +func getNextSunRiseOrSet(a *App, sunrise bool, offset ...types.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 diff --git a/checkers.go b/checkers.go index 2736416..0af8f4a 100644 --- a/checkers.go +++ b/checkers.go @@ -5,6 +5,7 @@ import ( "github.com/Xevion/go-ha/internal" "github.com/Xevion/go-ha/internal/parse" + "github.com/Xevion/go-ha/types" "github.com/golang-module/carbon" ) @@ -75,11 +76,11 @@ func checkExceptionDates(eList []time.Time) conditionCheck { return cc } -func checkExceptionRanges(eList []timeRange) conditionCheck { +func checkExceptionRanges(eList []types.TimeRange) conditionCheck { cc := conditionCheck{fail: false} now := time.Now() for _, eRange := range eList { - if now.After(eRange.start) && now.Before(eRange.end) { + if now.After(eRange.Start) && now.Before(eRange.End) { cc.fail = true break } @@ -161,7 +162,7 @@ func checkAllowlistDates(eList []time.Time) conditionCheck { return cc } -func checkStartEndTime(s TimeString, isStart bool) conditionCheck { +func checkStartEndTime(s types.TimeString, isStart bool) conditionCheck { cc := conditionCheck{fail: false} // pass immediately if default if s == "00:00" { diff --git a/checkers_test.go b/checkers_test.go index ae4c1d9..9065b82 100644 --- a/checkers_test.go +++ b/checkers_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/Xevion/go-ha/internal" + "github.com/Xevion/go-ha/types" "github.com/stretchr/testify/assert" ) @@ -15,16 +16,16 @@ type MockState struct { GetError bool } -func (s MockState) AfterSunrise(_ ...DurationString) bool { +func (s MockState) AfterSunrise(_ ...types.DurationString) bool { return true } -func (s MockState) BeforeSunrise(_ ...DurationString) bool { +func (s MockState) BeforeSunrise(_ ...types.DurationString) bool { return true } -func (s MockState) AfterSunset(_ ...DurationString) bool { +func (s MockState) AfterSunset(_ ...types.DurationString) bool { return true } -func (s MockState) BeforeSunset(_ ...DurationString) bool { +func (s MockState) BeforeSunset(_ ...types.DurationString) bool { return true } func (s MockState) Get(eid string) (EntityState, error) { diff --git a/cmd/generate/main.go b/cmd/generate/main.go index e21124f..cb2b7b3 100644 --- a/cmd/generate/main.go +++ b/cmd/generate/main.go @@ -10,6 +10,7 @@ import ( "text/template" ga "github.com/Xevion/go-ha" + "github.com/Xevion/go-ha/types" "gopkg.in/yaml.v3" ) @@ -98,7 +99,7 @@ func generate(config Config) error { config.HomeZoneEntityId = "zone.home" } - app, err := ga.NewApp(ga.NewAppRequest{ + app, err := ga.NewApp(types.NewAppRequest{ URL: config.URL, HAAuthToken: config.HAAuthToken, HomeZoneEntityId: config.HomeZoneEntityId, diff --git a/entity_listener.go b/entity_listener.go index a9f1fed..32bc2be 100644 --- a/entity_listener.go +++ b/entity_listener.go @@ -9,6 +9,7 @@ import ( "github.com/Xevion/go-ha/internal" "github.com/Xevion/go-ha/internal/parse" + "github.com/Xevion/go-ha/types" ) type EntityListener struct { @@ -26,7 +27,7 @@ type EntityListener struct { delayTimer *time.Timer exceptionDates []time.Time - exceptionRanges []timeRange + exceptionRanges []types.TimeRange runOnStartup bool runOnStartupCompleted bool @@ -127,13 +128,13 @@ func (b elBuilder3) ToState(s string) elBuilder3 { return b } -func (b elBuilder3) Duration(s DurationString) elBuilder3 { +func (b elBuilder3) Duration(s types.DurationString) elBuilder3 { d := parse.ParseDuration(string(s)) b.entityListener.delay = d return b } -func (b elBuilder3) Throttle(s DurationString) elBuilder3 { +func (b elBuilder3) Throttle(s types.DurationString) elBuilder3 { d := parse.ParseDuration(string(s)) b.entityListener.throttle = d return b @@ -145,7 +146,7 @@ func (b elBuilder3) ExceptionDates(t time.Time, tl ...time.Time) elBuilder3 { } func (b elBuilder3) ExceptionRange(start, end time.Time) elBuilder3 { - b.entityListener.exceptionRanges = append(b.entityListener.exceptionRanges, timeRange{start, end}) + b.entityListener.exceptionRanges = append(b.entityListener.exceptionRanges, types.TimeRange{start, end}) return b } diff --git a/event_listener.go b/event_listener.go index d86e832..3c483f4 100644 --- a/event_listener.go +++ b/event_listener.go @@ -10,6 +10,7 @@ import ( "github.com/Xevion/go-ha/internal" "github.com/Xevion/go-ha/internal/parse" ws "github.com/Xevion/go-ha/internal/websocket" + "github.com/Xevion/go-ha/types" ) type EventListener struct { @@ -21,7 +22,7 @@ type EventListener struct { lastRan carbon.Carbon exceptionDates []time.Time - exceptionRanges []timeRange + exceptionRanges []types.TimeRange enabledEntities []internal.EnabledDisabledInfo disabledEntities []internal.EnabledDisabledInfo @@ -80,7 +81,7 @@ func (b eventListenerBuilder3) OnlyBefore(end string) eventListenerBuilder3 { return b } -func (b eventListenerBuilder3) Throttle(s DurationString) eventListenerBuilder3 { +func (b eventListenerBuilder3) Throttle(s types.DurationString) eventListenerBuilder3 { d := parse.ParseDuration(string(s)) b.eventListener.throttle = d return b @@ -92,7 +93,7 @@ func (b eventListenerBuilder3) ExceptionDates(t time.Time, tl ...time.Time) even } func (b eventListenerBuilder3) ExceptionRange(start, end time.Time) eventListenerBuilder3 { - b.eventListener.exceptionRanges = append(b.eventListener.exceptionRanges, timeRange{start, end}) + b.eventListener.exceptionRanges = append(b.eventListener.exceptionRanges, types.TimeRange{start, end}) return b } diff --git a/internal/websocket/websocket.go b/internal/websocket/websocket.go index 835796b..af824f2 100644 --- a/internal/websocket/websocket.go +++ b/internal/websocket/websocket.go @@ -14,9 +14,8 @@ import ( "sync" "time" - "github.com/gorilla/websocket" - "github.com/Xevion/go-ha/internal" + "github.com/gorilla/websocket" ) var ErrInvalidToken = errors.New("invalid authentication token") diff --git a/interval.go b/interval.go index 311eda3..fbd7e30 100644 --- a/interval.go +++ b/interval.go @@ -7,6 +7,7 @@ import ( "github.com/Xevion/go-ha/internal" "github.com/Xevion/go-ha/internal/parse" + "github.com/Xevion/go-ha/types" ) type IntervalCallback func(*Service, State) @@ -14,12 +15,12 @@ type IntervalCallback func(*Service, State) type Interval struct { frequency time.Duration callback IntervalCallback - startTime TimeString - endTime TimeString + startTime types.TimeString + endTime types.TimeString nextRunTime time.Time exceptionDates []time.Time - exceptionRanges []timeRange + exceptionRanges []types.TimeRange enabledEntities []internal.EnabledDisabledInfo disabledEntities []internal.EnabledDisabledInfo @@ -63,7 +64,7 @@ func (i Interval) String() string { ) } -func formatStartOrEndString(s TimeString, isStart bool) string { +func formatStartOrEndString(s types.TimeString, isStart bool) string { if s == "00:00" { return "" } @@ -80,20 +81,20 @@ func (ib intervalBuilder) Call(callback IntervalCallback) intervalBuilderCall { } // Takes a DurationString ("2h", "5m", etc) to set the frequency of the interval. -func (ib intervalBuilderCall) Every(s DurationString) intervalBuilderEnd { +func (ib intervalBuilderCall) Every(s types.DurationString) intervalBuilderEnd { d := parse.ParseDuration(string(s)) ib.interval.frequency = d return intervalBuilderEnd(ib) } // Takes a TimeString ("HH:MM") when this interval will start running for the day. -func (ib intervalBuilderEnd) StartingAt(s TimeString) intervalBuilderEnd { +func (ib intervalBuilderEnd) StartingAt(s types.TimeString) intervalBuilderEnd { ib.interval.startTime = s return ib } // Takes a TimeString ("HH:MM") when this interval will stop running for the day. -func (ib intervalBuilderEnd) EndingAt(s TimeString) intervalBuilderEnd { +func (ib intervalBuilderEnd) EndingAt(s types.TimeString) intervalBuilderEnd { ib.interval.endTime = s return ib } @@ -104,7 +105,7 @@ func (ib intervalBuilderEnd) ExceptionDates(t time.Time, tl ...time.Time) interv } func (ib intervalBuilderEnd) ExceptionRange(start, end time.Time) intervalBuilderEnd { - ib.interval.exceptionRanges = append(ib.interval.exceptionRanges, timeRange{start, end}) + ib.interval.exceptionRanges = append(ib.interval.exceptionRanges, types.TimeRange{start, end}) return ib } diff --git a/schedule.go b/schedule.go index 602a1cd..57370b9 100644 --- a/schedule.go +++ b/schedule.go @@ -7,6 +7,7 @@ import ( "github.com/Xevion/go-ha/internal" "github.com/Xevion/go-ha/internal/parse" + "github.com/Xevion/go-ha/types" "github.com/golang-module/carbon" ) @@ -23,7 +24,7 @@ type DailySchedule struct { isSunrise bool isSunset bool - sunOffset DurationString + sunOffset types.DurationString exceptionDates []time.Time allowlistDates []time.Time @@ -85,7 +86,7 @@ func (sb scheduleBuilderCall) At(s string) scheduleBuilderEnd { // Sunrise takes 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 scheduleBuilderCall) Sunrise(offset ...DurationString) scheduleBuilderEnd { +func (sb scheduleBuilderCall) Sunrise(offset ...types.DurationString) scheduleBuilderEnd { sb.schedule.isSunrise = true if len(offset) > 0 { sb.schedule.sunOffset = offset[0] @@ -96,7 +97,7 @@ func (sb scheduleBuilderCall) Sunrise(offset ...DurationString) scheduleBuilderE // Sunset takes 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 scheduleBuilderCall) Sunset(offset ...DurationString) scheduleBuilderEnd { +func (sb scheduleBuilderCall) Sunset(offset ...types.DurationString) scheduleBuilderEnd { sb.schedule.isSunset = true if len(offset) > 0 { sb.schedule.sunOffset = offset[0] diff --git a/state.go b/state.go index 183e8c7..10c0554 100644 --- a/state.go +++ b/state.go @@ -9,13 +9,14 @@ import ( "github.com/golang-module/carbon" internal "github.com/Xevion/go-ha/internal" + "github.com/Xevion/go-ha/types" ) type State interface { - AfterSunrise(...DurationString) bool - BeforeSunrise(...DurationString) bool - AfterSunset(...DurationString) bool - BeforeSunset(...DurationString) bool + AfterSunrise(...types.DurationString) bool + BeforeSunrise(...types.DurationString) bool + AfterSunset(...types.DurationString) bool + BeforeSunset(...types.DurationString) bool ListEntities() ([]EntityState, error) Get(entityId string) (EntityState, error) Equals(entityId, state string) (bool, error) @@ -99,20 +100,20 @@ func (s *StateImpl) Equals(entityId string, expectedState string) (bool, error) return currentState.State == expectedState, nil } -func (s *StateImpl) BeforeSunrise(offset ...DurationString) bool { +func (s *StateImpl) BeforeSunrise(offset ...types.DurationString) bool { sunrise := getSunriseSunset(s /* sunrise = */, true, carbon.Now(), offset...) return carbon.Now().Lt(sunrise) } -func (s *StateImpl) AfterSunrise(offset ...DurationString) bool { +func (s *StateImpl) AfterSunrise(offset ...types.DurationString) bool { return !s.BeforeSunrise(offset...) } -func (s *StateImpl) BeforeSunset(offset ...DurationString) bool { +func (s *StateImpl) BeforeSunset(offset ...types.DurationString) bool { sunset := getSunriseSunset(s /* sunrise = */, false, carbon.Now(), offset...) return carbon.Now().Lt(sunset) } -func (s *StateImpl) AfterSunset(offset ...DurationString) bool { +func (s *StateImpl) AfterSunset(offset ...types.DurationString) bool { return !s.BeforeSunset(offset...) } diff --git a/types/app.go b/types/app.go new file mode 100644 index 0000000..164e449 --- /dev/null +++ b/types/app.go @@ -0,0 +1,35 @@ +package types + +// NewAppRequest contains the configuration for creating a new App instance. +type NewAppRequest struct { + // Required + URL string + + // Optional + // Deprecated: use URL instead + // IpAddress of your Home Assistant instance i.e. "localhost" + // or "192.168.86.59" etc. + IpAddress string + + // Optional + // Deprecated: use URL instead + // 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 + + // Optional + // Whether to use secure connections for http and websockets. + // Setting this to `true` will use `https://` instead of `https://` + // and `wss://` instead of `ws://`. + Secure bool +} diff --git a/types/common.go b/types/common.go new file mode 100644 index 0000000..048b5f7 --- /dev/null +++ b/types/common.go @@ -0,0 +1,22 @@ +package types + +import "time" + +// DurationString represents a duration, such as "2s" or "24h". +// See https://pkg.go.dev/time#ParseDuration for all valid time units. +type DurationString string + +// TimeString is a 24-hr format time "HH:MM" such as "07:30". +type TimeString string + +// TimeRange represents a time range with start and end times. +type TimeRange struct { + Start time.Time + End time.Time +} + +// Item represents a priority queue item with a value and priority. +type Item struct { + Value interface{} + Priority float64 +} diff --git a/types/request_types.go b/types/request.go similarity index 100% rename from types/request_types.go rename to types/request.go