Skip to content

Commit

Permalink
simplify next occurance handling, reduce public api
Browse files Browse the repository at this point in the history
  • Loading branch information
naltatis committed Nov 19, 2024
1 parent 12d941b commit 645c1f9
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 150 deletions.
46 changes: 0 additions & 46 deletions api/plans.go
Original file line number Diff line number Diff line change
@@ -1,54 +1,8 @@
package api

import (
"time"
)

type PlanStruct struct {
Id int `json:"Id"`
Soc int `json:"soc"`
Time time.Time `json:"time"`
Active bool `json:"active"`
}

type RepeatingPlanStruct struct {
Weekdays []int `json:"weekdays"`
Time string `json:"time"`
Soc int `json:"soc"`
Active bool `json:"active"`
}

func (p *RepeatingPlanStruct) ToPlansWithTimestamp(id int) []PlanStruct {
var formattedPlans []PlanStruct

now := time.Now()

planTime, err := time.Parse("15:04", p.Time)
if err != nil {
return []PlanStruct{}
}

for _, w := range p.Weekdays {
// Calculate the difference in days to the target weekday
dayOffset := (w - int(now.Weekday()) + 7) % 7

// If the user has selected the day of the week that is today, and at the same time the user
// has selected a time that would be in the past for today, the next day of the week in a week should be used
if dayOffset == 0 && (now.UTC().Hour()*60+now.UTC().Minute()) > (planTime.Hour()*60+planTime.Minute()) {
dayOffset = 7
}

// Adjust the current timestamp to the target weekday and set the time
timestamp := now.AddDate(0, 0, dayOffset).Truncate(24 * time.Hour).Add(time.Hour*time.Duration(planTime.Hour()) + time.Minute*time.Duration(planTime.Minute()))

// Append the resulting plan with the calculated timestamp
formattedPlans = append(formattedPlans, PlanStruct{
Id: id,
Soc: p.Soc,
Time: timestamp,
Active: p.Active,
})
}

return formattedPlans
}
60 changes: 33 additions & 27 deletions core/loadpoint_effective.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/core/keys"
"github.com/evcc-io/evcc/core/vehicle"
"github.com/evcc-io/evcc/util"
)

// PublishEffectiveValues publishes all effective values
Expand All @@ -30,38 +31,44 @@ func (lp *Loadpoint) EffectivePriority() int {
return lp.GetPriority()
}

// nextVehiclePlan returns the next vehicle plan time, soc and ID
// nextVehiclePlan returns the next vehicle plan time, soc and id
func (lp *Loadpoint) nextVehiclePlan() (time.Time, int, int) {
if v := lp.GetVehicle(); v != nil {
// merge the static plan with the active repeating ones to sort them in one array

var plans = vehicle.Settings(lp.log, v).GetRepeatingPlansWithTimestamps()
type plan struct {
Time time.Time
Soc int
Id int
}

planTime, soc := vehicle.Settings(lp.log, v).GetStaticPlanSoc()
var plans []plan

if soc != 0 {
plans = append(plans, api.PlanStruct{
Id: 1, // id of the static plan is always 1
Soc: soc,
Time: planTime,
Active: true,
})
// static plan
if planTime, soc := vehicle.Settings(lp.log, v).GetPlanSoc(); soc != 0 {
plans = append(plans, plan{Id: 1, Soc: soc, Time: planTime})
}

// Filter out inactive plans
activePlans := []api.PlanStruct{}
for _, plan := range plans {
if plan.Active {
activePlans = append(activePlans, plan)
// repeating plans
for index, rp := range vehicle.Settings(lp.log, v).GetRepeatingPlans() {
if !rp.Active {
continue
}

time, err := util.GetNextOccurrence(rp.Weekdays, rp.Time)
if err != nil {
lp.log.DEBUG.Printf("invalid repeating plan: weekdays=%v, time=%s, error=%v", rp.Weekdays, rp.Time, err)
continue
}

plans = append(plans, plan{Id: index + 2, Soc: rp.Soc, Time: time})
}

sort.Slice(activePlans, func(i, j int) bool {
return activePlans[i].Time.Before(activePlans[j].Time)
// sort plans by time
sort.Slice(plans, func(i, j int) bool {
return plans[i].Time.Before(plans[j].Time)
})

if len(activePlans) > 0 {
return activePlans[0].Time, activePlans[0].Soc, activePlans[0].Id
if len(plans) > 0 {
return plans[0].Time, plans[0].Soc, plans[0].Id
}
}
return time.Time{}, 0, 0
Expand All @@ -78,13 +85,12 @@ func (lp *Loadpoint) EffectivePlanId() int {
if lp.socBasedPlanning() {
_, _, id := lp.nextVehiclePlan()
return id
} else {
if lp.planEnergy != 0 {
return 1
} else {
return 0
}
}
if lp.planEnergy > 0 {
return 1
}
// no plan
return 0
}

// EffectivePlanTime returns the effective plan time
Expand Down
15 changes: 11 additions & 4 deletions core/site_vehicles.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package core

import (
"time"

"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/core/keys"
"github.com/evcc-io/evcc/core/site"
Expand All @@ -10,6 +12,11 @@ import (
"github.com/samber/lo"
)

type planStruct struct {
Soc int `json:"soc"`
Time time.Time `json:"time"`
}

type vehicleStruct struct {
Title string `json:"title"`
Icon string `json:"icon,omitempty"`
Expand All @@ -21,7 +28,7 @@ type vehicleStruct struct {
MaxCurrent float64 `json:"maxCurrent,omitempty"`
Priority int `json:"priority,omitempty"`
Features []string `json:"features,omitempty"`
Plans []api.PlanStruct `json:"plans,omitempty"`
Plans []planStruct `json:"plans,omitempty"`
RepeatingPlans []api.RepeatingPlanStruct `json:"repeatingPlans"`
}

Expand All @@ -31,11 +38,11 @@ func (site *Site) publishVehicles() {
res := make(map[string]vehicleStruct, len(vv))

for _, v := range vv {
var plans []api.PlanStruct
var plans []planStruct

// TODO: add support for multiple plans
if time, soc := v.GetStaticPlanSoc(); !time.IsZero() {
plans = append(plans, api.PlanStruct{Soc: soc, Time: time})
if time, soc := v.GetPlanSoc(); !time.IsZero() {
plans = append(plans, planStruct{Soc: soc, Time: time})
}

instance := v.Instance()
Expand Down
25 changes: 13 additions & 12 deletions core/vehicle/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ func (v *adapter) SetLimitSoc(soc int) {
v.publish()
}

// GetStaticPlanSoc returns the charge plan soc
func (v *adapter) GetStaticPlanSoc() (time.Time, int) {
// GetPlanSoc returns the charge plan soc
func (v *adapter) GetPlanSoc() (time.Time, int) {
var ts time.Time
if v, err := settings.Time(v.key() + keys.PlanTime); err == nil {
ts = v
Expand Down Expand Up @@ -105,6 +105,17 @@ func (v *adapter) SetPlanSoc(ts time.Time, soc int) error {
}

func (v *adapter) SetRepeatingPlans(plans []api.RepeatingPlanStruct) error {
for _, plan := range plans {
for _, day := range plan.Weekdays {
if day < 0 || day > 6 {
return fmt.Errorf("weekday out of range: %v", day)
}
}
if _, err := time.Parse("15:04", plan.Time); err != nil {
return fmt.Errorf("invalid time: %v", err)
}
}

v.log.DEBUG.Printf("update repeating plans for %s to: %v", v.name, plans)

settings.SetJson(v.key()+keys.RepeatingPlans, plans)
Expand All @@ -124,13 +135,3 @@ func (v *adapter) GetRepeatingPlans() []api.RepeatingPlanStruct {

return []api.RepeatingPlanStruct{}
}

func (v *adapter) GetRepeatingPlansWithTimestamps() []api.PlanStruct {
var formattedPlans []api.PlanStruct

for id, p := range v.GetRepeatingPlans() {
formattedPlans = append(formattedPlans, p.ToPlansWithTimestamp(id+2)...)
}

return formattedPlans
}
6 changes: 2 additions & 4 deletions core/vehicle/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,15 @@ type API interface {
// SetLimitSoc sets the limit soc
SetLimitSoc(soc int)

// GetStaticPlanSoc returns the charge plan soc
GetStaticPlanSoc() (time.Time, int)
// GetPlanSoc returns the charge plan soc
GetPlanSoc() (time.Time, int)
// SetPlanSoc sets the charge plan time and soc
SetPlanSoc(time.Time, int) error

// GetRepeatingPlans returns every repeating plan
GetRepeatingPlans() []api.RepeatingPlanStruct
// SetRepeatingPlans stores every repeating plan
SetRepeatingPlans([]api.RepeatingPlanStruct) error
// GetRepeatingPlansAsNormalPlans returns every repeating plan with a timestamp-key to sort
GetRepeatingPlansWithTimestamps() []api.PlanStruct

// // GetMinCurrent returns the min charging current
// GetMinCurrent() float64
Expand Down
8 changes: 2 additions & 6 deletions core/vehicle/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ func (v *dummy) GetLimitSoc() int {
func (v *dummy) SetLimitSoc(soc int) {
}

// GetStaticPlanSoc returns the charge plan soc
func (v *dummy) GetStaticPlanSoc() (time.Time, int) {
// GetPlanSoc returns the charge plan soc
func (v *dummy) GetPlanSoc() (time.Time, int) {
return time.Time{}, 0
}

Expand All @@ -56,7 +56,3 @@ func (v *dummy) SetRepeatingPlans(plans []api.RepeatingPlanStruct) error {
func (v *dummy) GetRepeatingPlans() []api.RepeatingPlanStruct {
return []api.RepeatingPlanStruct{}
}

func (v *dummy) GetRepeatingPlansWithTimestamps() []api.PlanStruct {
return []api.PlanStruct{}
}
44 changes: 15 additions & 29 deletions core/vehicle/mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion push/hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (h *Hub) apply(ev Event, tmpl string) (string, error) {
if v, err := h.vehicles.ByName(name); err == nil {
attr["vehicleLimitSoc"] = v.GetLimitSoc()
attr["vehicleMinSoc"] = v.GetMinSoc()
attr["vehiclePlanTime"], attr["vehiclePlanSoc"] = v.GetStaticPlanSoc()
attr["vehiclePlanTime"], attr["vehiclePlanSoc"] = v.GetPlanSoc()

instance := v.Instance()
attr["vehicleTitle"] = instance.Title()
Expand Down
2 changes: 1 addition & 1 deletion server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, valueChan chan<- util.Param)
"maxcurrent": {"POST", "/maxcurrent/{value:[0-9.]+}", floatHandler(lp.SetMaxCurrent, lp.GetMaxCurrent)},
"phases": {"POST", "/phases/{value:[0-9]+}", intHandler(lp.SetPhases, lp.GetPhases)},
"plan": {"GET", "/plan", planHandler(lp)},
"staticPlanpreview": {"GET", "/plan/static/preview/{type:(?:soc|energy)}/{value:[0-9.]+}/{time:[0-9TZ:.+-]+}", staticPlanPreviewHandler(lp)},
"staticPlanPreview": {"GET", "/plan/static/preview/{type:(?:soc|energy)}/{value:[0-9.]+}/{time:[0-9TZ:.+-]+}", staticPlanPreviewHandler(lp)},
"repeatingPlanPreview": {"GET", "/plan/repeating/preview/{weekdays:[0-9,]+}/{time:[0-2][0-9]:[0-5][0-9]}/{soc:[0-9]+}", repeatingPlanPreviewHandler(lp)},
"planenergy": {"POST", "/plan/energy/{value:[0-9.]+}/{time:[0-9TZ:.+-]+}", planEnergyHandler(lp)},
"planenergy2": {"DELETE", "/plan/energy", planRemoveHandler(lp)},
Expand Down
Loading

0 comments on commit 645c1f9

Please sign in to comment.