evcc-io/vehicle/homeassistant.go

141 lines
3.5 KiB
Go

package vehicle
import (
"errors"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/homeassistant"
)
type HomeAssistant struct {
*embed
conn *homeassistant.Connection
soc string
}
// Register on startup
func init() {
registry.Add("homeassistant", NewHomeAssistantVehicleFromConfig)
}
// Constructor from YAML config
func NewHomeAssistantVehicleFromConfig(other map[string]any) (api.Vehicle, error) {
var cc struct {
embed `mapstructure:",squash"`
URI string
Token_ string `mapstructure:"token"` // TODO deprecated
Home string // TODO deprecated
Sensors struct {
Soc string // required
Range string // optional
Status string // optional
LimitSoc string // optional
Odometer string // optional
Climater string // optional
FinishTime string // optional
}
Services struct {
Start string `mapstructure:"start_charging"` // script.* optional
Stop string `mapstructure:"stop_charging"` // script.* optional
Wakeup string // script.* optional
SetMaxCurrent string // number.* or input_number.* optional
}
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
if cc.Sensors.Soc == "" {
return nil, errors.New("missing soc sensor")
}
log := util.NewLogger("ha-vehicle")
conn, err := homeassistant.NewConnection(log, cc.URI, cc.Home)
if err != nil {
return nil, err
}
res := &HomeAssistant{
embed: &cc.embed,
conn: conn,
soc: cc.Sensors.Soc,
}
// optional features
var (
limitSoc func() (int64, error)
status func() (api.ChargeStatus, error)
rng func() (int64, error)
odo func() (float64, error)
climater func() (bool, error)
finish func() (time.Time, error)
enable func(bool) error
wakeup func() error
maxcurrent func(int64) error
)
if cc.Sensors.LimitSoc != "" {
limitSoc = func() (int64, error) {
f, err := conn.GetFloatState(cc.Sensors.LimitSoc)
return int64(f), err
}
}
if cc.Sensors.Status != "" {
status = func() (api.ChargeStatus, error) { return conn.GetChargeStatus(cc.Sensors.Status) }
}
if cc.Sensors.Range != "" {
rng = func() (int64, error) {
f, err := conn.GetFloatState(cc.Sensors.Range)
return int64(f), err
}
}
if cc.Sensors.Odometer != "" {
odo = func() (float64, error) { return conn.GetFloatState(cc.Sensors.Odometer) }
}
if cc.Sensors.Climater != "" {
climater = func() (bool, error) { return conn.GetBoolState(cc.Sensors.Climater) }
}
if cc.Sensors.FinishTime != "" {
finish = func() (time.Time, error) { return conn.GetTimeState(cc.Sensors.FinishTime) }
}
if cc.Services.Start != "" && cc.Services.Stop != "" {
enable = func(enable bool) error { return res.enable(cc.Services.Start, cc.Services.Stop, enable) }
}
if cc.Services.Wakeup != "" {
wakeup = func() error { return conn.CallSwitchService(cc.Services.Wakeup, true) }
}
if cc.Services.SetMaxCurrent != "" {
maxcurrent = func(current int64) error { return conn.CallNumberService(cc.Services.SetMaxCurrent, float64(current)) }
}
// decorate all features
return decorateVehicle(
res,
limitSoc,
status,
rng,
odo,
climater,
maxcurrent,
nil,
finish,
wakeup,
enable,
), nil
}
func (v *HomeAssistant) Soc() (float64, error) {
return v.conn.GetFloatState(v.soc)
}
func (v *HomeAssistant) enable(on, off string, enable bool) error {
if enable {
return v.conn.CallSwitchService(on, true)
}
return v.conn.CallSwitchService(off, true)
}