mirror of
https://github.com/Xevion/go-ha.git
synced 2025-12-06 05:15:15 -06:00
115 lines
3.1 KiB
Go
115 lines
3.1 KiB
Go
package scheduling
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/Xevion/go-ha/types"
|
|
)
|
|
|
|
type DailyScheduleBuilder struct {
|
|
errors []error
|
|
hashes map[uint64]bool
|
|
triggers []Trigger
|
|
}
|
|
|
|
func NewSchedule() *DailyScheduleBuilder {
|
|
return &DailyScheduleBuilder{
|
|
hashes: make(map[uint64]bool),
|
|
}
|
|
}
|
|
|
|
// tryAddTrigger adds a trigger to the builder if it is not already present.
|
|
// If the trigger is already present, an error will be added to the builder's errors.
|
|
// It will return the builder for chaining.
|
|
func (b *DailyScheduleBuilder) tryAddTrigger(trigger Trigger) *DailyScheduleBuilder {
|
|
hash := trigger.Hash()
|
|
if _, ok := b.hashes[hash]; ok {
|
|
b.errors = append(b.errors, fmt.Errorf("duplicate trigger: %v", trigger))
|
|
return b
|
|
}
|
|
|
|
b.triggers = append(b.triggers, trigger)
|
|
b.hashes[hash] = true
|
|
|
|
return b
|
|
}
|
|
|
|
func (b *DailyScheduleBuilder) onSun(sunset bool, offset ...types.DurationString) *DailyScheduleBuilder {
|
|
if len(offset) == 0 {
|
|
b.errors = append(b.errors, fmt.Errorf("no offset provided for sun"))
|
|
return b
|
|
}
|
|
|
|
offsetDuration, err := time.ParseDuration(string(offset[0]))
|
|
if err != nil {
|
|
b.errors = append(b.errors, err)
|
|
return b
|
|
}
|
|
|
|
return b.tryAddTrigger(&SunTrigger{
|
|
sunset: sunset,
|
|
offset: &offsetDuration,
|
|
})
|
|
}
|
|
|
|
// OnSunrise adds a trigger for sunrise with an optional offset.
|
|
// Only the first offset is considered.
|
|
// You can call this multiple times to add multiple triggers for sunrise with different offsets.
|
|
func (b *DailyScheduleBuilder) OnSunrise(offset ...types.DurationString) *DailyScheduleBuilder {
|
|
return b.onSun(false, offset...)
|
|
}
|
|
|
|
// OnSunset adds a trigger for sunset with an optional offset.
|
|
// Only the first offset is considered.
|
|
func (b *DailyScheduleBuilder) OnSunset(offset ...types.DurationString) *DailyScheduleBuilder {
|
|
return b.onSun(true, offset...)
|
|
}
|
|
|
|
// OnFixedTime adds a trigger for a fixed time each day.
|
|
// The time is in the local timezone.
|
|
// This will error if the integer values are not in the range 0-23 for the hour and 0-59 for the minute.
|
|
func (b *DailyScheduleBuilder) OnFixedTime(hour, minute int) *DailyScheduleBuilder {
|
|
errored := false
|
|
if hour < 0 || hour > 23 {
|
|
b.errors = append(b.errors, fmt.Errorf("hour must be between 0 and 23"))
|
|
errored = true
|
|
}
|
|
|
|
if minute < 0 || minute > 59 {
|
|
b.errors = append(b.errors, fmt.Errorf("minute must be between 0 and 59"))
|
|
errored = true
|
|
}
|
|
|
|
if errored {
|
|
return b
|
|
}
|
|
|
|
return b.tryAddTrigger(&FixedTimeTrigger{
|
|
Hour: hour,
|
|
Minute: minute,
|
|
})
|
|
}
|
|
|
|
// Build returns a Trigger that will trigger at the configured times.
|
|
// It will return an error if any errors occurred during configuration.
|
|
func (b *DailyScheduleBuilder) Build() (Trigger, error) {
|
|
// If there are no triggers, add an error.
|
|
if len(b.triggers) == 0 {
|
|
b.errors = append(b.errors, fmt.Errorf("no triggers provided"))
|
|
}
|
|
|
|
// If there are errors, return an error.
|
|
if len(b.errors) > 0 {
|
|
return nil, fmt.Errorf("errors occurred: %v", b.errors)
|
|
}
|
|
|
|
// If there is only one trigger, return it.
|
|
if len(b.triggers) == 1 {
|
|
return b.triggers[0], nil
|
|
}
|
|
|
|
// Otherwise, return a composite schedule that combines all the triggers.
|
|
return &CompositeDailySchedule{triggers: b.triggers}, nil
|
|
}
|