evcc-io/vehicle/fiat/controller.go
2025-05-29 09:49:50 +02:00

121 lines
3.7 KiB
Go

package fiat
import (
"fmt"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
)
type Controller struct {
pvd *Provider
api *API
log *util.Logger
vin string
pin string
}
// NewController creates a vehicle current and charge controller
func NewController(provider *Provider, api *API, log *util.Logger, vin string, pin string) *Controller {
impl := &Controller{
pvd: provider,
api: api,
log: log,
vin: vin,
pin: pin,
}
return impl
}
var _ api.CurrentController = (*Controller)(nil)
// MaxCurrent implements the api.CurrentController interface
func (c *Controller) MaxCurrent(current int64) error {
// Even if we cannot control the current, this interface must be implemented otherwise the ChargeEnable is never called
return nil
}
var _ api.ChargeController = (*Controller)(nil)
// ChargeEnable implements the api.ChargeController interface
func (c *Controller) ChargeEnable(enable bool) error {
if c.pin == "" {
return api.ErrMissingCredentials
}
// get current schedule status from provider (cached)
stat, err := c.pvd.statusG()
if err != nil {
return err
}
if stat.EvInfo == nil || stat.EvInfo.Schedules == nil || len(stat.EvInfo.Schedules) == 0 {
return api.ErrNotAvailable
}
// configure first schedule and make sure it's active
c.configureChargeSchedule(&stat.EvInfo.Schedules[0])
if enable {
// start charging by updating active charge schedule to start now and end in 12h
stat.EvInfo.Schedules[0].StartTime = time.Now().Format("15:04") // only hour and minutes
stat.EvInfo.Schedules[0].EndTime = time.Now().Add(time.Hour * 12).Format("15:04") // only hour and minutes
} else {
// stop charging by updating active charge schedule end time to now
stat.EvInfo.Schedules[0].EndTime = time.Now().Format("15:04") // only hour and minutes
}
// make sure the other charge schedules are disabled in case user changed them
c.disableConflictingChargeSchedule(&stat.EvInfo.Schedules[1])
c.disableConflictingChargeSchedule(&stat.EvInfo.Schedules[2])
// post new schedule
res, err := c.api.UpdateSchedule(c.vin, c.pin, stat.EvInfo.Schedules)
if err == nil && res.ResponseStatus != "pending" {
err = fmt.Errorf("invalid response status: %s", res.ResponseStatus)
}
return err
}
func (c *Controller) configureChargeSchedule(schedule *Schedule) {
// all values are set to be sure no manual change can lead to inconsistencies
schedule.CabinPriority = false
schedule.ChargeToFull = false
schedule.EnableScheduleType = true
schedule.RepeatSchedule = true
schedule.ScheduleType = "CHARGE"
// only enable for current day to avoid undesired charge start in the future
weekday := time.Now().Weekday()
schedule.ScheduledDays.Monday = (weekday == time.Monday)
schedule.ScheduledDays.Tuesday = (weekday == time.Tuesday)
schedule.ScheduledDays.Wednesday = (weekday == time.Wednesday)
schedule.ScheduledDays.Thursday = (weekday == time.Thursday)
schedule.ScheduledDays.Friday = (weekday == time.Friday)
schedule.ScheduledDays.Saturday = (weekday == time.Saturday)
schedule.ScheduledDays.Sunday = (weekday == time.Sunday)
}
func (c *Controller) disableConflictingChargeSchedule(schedule *Schedule) {
// make sure the other charge schedules are disabled in case user changed them
if schedule.ScheduleType == "CHARGE" && schedule.EnableScheduleType {
schedule.EnableScheduleType = false
}
}
var _ api.Resurrector = (*Controller)(nil)
func (c *Controller) WakeUp() error {
if c.pin == "" {
c.log.DEBUG.Printf("pin required for vehicle wakeup")
return nil
}
res, err := c.api.ChargeNow(c.vin, c.pin)
if err == nil && res.ResponseStatus != "pending" {
err = fmt.Errorf("invalid response status: %s", res.ResponseStatus)
}
return err
}