package server import ( "errors" "fmt" "net/http" "strconv" "strings" "time" "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/core/site" "github.com/evcc-io/evcc/util" "github.com/gorilla/mux" ) // remoteDemandHandler updates minimum soc func remoteDemandHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) source := vars["source"] demand, err := loadpoint.RemoteDemandString(vars["demand"]) if err != nil { jsonError(w, http.StatusBadRequest, err) return } lp.RemoteControl(source, demand) res := struct { Demand loadpoint.RemoteDemand `json:"demand"` Source string `json:"source"` }{ Source: source, Demand: demand, } jsonResult(w, res) } } // planHandler returns the current plan func planHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { maxPower := lp.EffectiveMaxPower() planTime := lp.EffectivePlanTime() id := lp.EffectivePlanId() goal, _ := lp.GetPlanGoal() precondition := lp.GetPlanPreCondDuration() requiredDuration := lp.GetPlanRequiredDuration(goal, maxPower) plan := lp.GetPlan(planTime, requiredDuration, precondition) res := struct { PlanId int `json:"planId"` PlanTime time.Time `json:"planTime"` Duration int64 `json:"duration"` Precondition int64 `json:"precondition"` Plan api.Rates `json:"plan"` Power float64 `json:"power"` }{ PlanId: id, PlanTime: planTime, Duration: int64(requiredDuration.Seconds()), Precondition: int64(precondition.Seconds()), Plan: plan, Power: maxPower, } jsonResult(w, res) } } // staticPlanPreviewHandler returns a plan preview for given parameters func staticPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) query := r.URL.Query() planTime, err := time.ParseInLocation(time.RFC3339, vars["time"], nil) if err != nil { jsonError(w, http.StatusBadRequest, err) return } goal, err := strconv.ParseFloat(vars["value"], 64) if err != nil { jsonError(w, http.StatusBadRequest, err) return } precondition, err := parseDuration(query.Get("precondition")) if err != nil { jsonError(w, http.StatusBadRequest, err) return } switch typ := vars["type"]; typ { case "soc": if !lp.SocBasedPlanning() { jsonError(w, http.StatusBadRequest, errors.New("soc planning only available for vehicles with known soc and capacity")) return } case "energy": if lp.SocBasedPlanning() { jsonError(w, http.StatusBadRequest, errors.New("energy planning not available for vehicles with known soc and capacity")) return } default: jsonError(w, http.StatusBadRequest, fmt.Errorf("invalid plan type: %s", typ)) return } maxPower := lp.EffectiveMaxPower() requiredDuration := lp.GetPlanRequiredDuration(goal, maxPower) plan := lp.GetPlan(planTime, requiredDuration, precondition) res := struct { PlanTime time.Time `json:"planTime"` Duration int64 `json:"duration"` Precondition int64 `json:"precondition"` Plan api.Rates `json:"plan"` Power float64 `json:"power"` }{ PlanTime: planTime, Duration: int64(requiredDuration.Seconds()), Precondition: int64(precondition.Seconds()), Plan: plan, Power: maxPower, } jsonResult(w, res) } } func repeatingPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) query := r.URL.Query() hourMinute := vars["time"] tz := vars["tz"] var weekdays []int for _, weekdayStr := range strings.Split(vars["weekdays"], ",") { weekday, err := strconv.Atoi(weekdayStr) if err != nil { jsonError(w, http.StatusBadRequest, fmt.Errorf("invalid weekdays format")) return } weekdays = append(weekdays, weekday) } soc, err := strconv.ParseFloat(vars["soc"], 64) if err != nil { jsonError(w, http.StatusBadRequest, err) return } planTime, err := util.GetNextOccurrence(weekdays, hourMinute, tz) if err != nil { jsonError(w, http.StatusBadRequest, err) return } precondition, err := parseDuration(query.Get("precondition")) if err != nil { jsonError(w, http.StatusBadRequest, err) return } maxPower := lp.EffectiveMaxPower() requiredDuration := lp.GetPlanRequiredDuration(soc, maxPower) plan := lp.GetPlan(planTime, requiredDuration, precondition) res := struct { PlanTime time.Time `json:"planTime"` Duration int64 `json:"duration"` Precondition int64 `json:"precondition"` Plan api.Rates `json:"plan"` Power float64 `json:"power"` }{ PlanTime: planTime, Duration: int64(requiredDuration.Seconds()), Precondition: int64(precondition.Seconds()), Plan: plan, Power: maxPower, } jsonResult(w, res) } } // planEnergyHandler updates plan energy and time func planEnergyHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) query := r.URL.Query() ts, err := time.ParseInLocation(time.RFC3339, vars["time"], nil) if err != nil { jsonError(w, http.StatusBadRequest, err) return } val, err := strconv.ParseFloat(vars["value"], 64) if err != nil { jsonError(w, http.StatusBadRequest, err) return } precondition, err := parseDuration(query.Get("precondition")) if err != nil { jsonError(w, http.StatusBadRequest, err) return } if err := lp.SetPlanEnergy(ts, precondition, val); err != nil { jsonError(w, http.StatusBadRequest, err) return } ts, precondition, energy := lp.GetPlanEnergy() res := struct { Energy float64 `json:"energy"` Precondition int64 `json:"precondition"` Time time.Time `json:"time"` }{ Energy: energy, Precondition: int64(precondition.Seconds()), Time: ts, } jsonResult(w, res) } } // planRemoveHandler removes plan time func planRemoveHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if err := lp.SetPlanEnergy(time.Time{}, 0, 0); err != nil { jsonError(w, http.StatusBadRequest, err) return } res := struct{}{} jsonResult(w, res) } } // vehicleSelectHandler sets active vehicle func vehicleSelectHandler(site site.API, lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) vv, err := site.Vehicles().ByName(vars["name"]) if err != nil { jsonError(w, http.StatusBadRequest, err) return } v := vv.Instance() lp.SetVehicle(v) res := struct { Vehicle string `json:"vehicle"` }{ Vehicle: v.GetTitle(), } jsonResult(w, res) } } // vehicleRemoveHandler removes vehicle func vehicleRemoveHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { lp.SetVehicle(nil) res := struct{}{} jsonResult(w, res) } } // vehicleDetectHandler starts vehicle detection func vehicleDetectHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { lp.StartVehicleDetection() res := struct{}{} jsonResult(w, res) } }