132 lines
2.7 KiB
Go
132 lines
2.7 KiB
Go
package vehicle
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/evcc-io/evcc/api"
|
|
"github.com/evcc-io/evcc/util"
|
|
"github.com/evcc-io/evcc/vehicle/octopuskraken"
|
|
)
|
|
|
|
// OctopusDe is an api.Vehicle implementation for the Octopus Energy Germany Kraken API
|
|
type OctopusDe struct {
|
|
*embed
|
|
*octopuskraken.API
|
|
account string
|
|
device string
|
|
deviceID string
|
|
dataG func() (octopuskraken.Device, error)
|
|
}
|
|
|
|
func init() {
|
|
registry.Add("octopus-de", NewOctopusDeFromConfig)
|
|
}
|
|
|
|
// NewOctopusDeFromConfig creates a new vehicle
|
|
func NewOctopusDeFromConfig(other map[string]any) (api.Vehicle, error) {
|
|
cc := struct {
|
|
embed `mapstructure:",squash"`
|
|
Email string
|
|
Password string
|
|
AccountNumber string
|
|
Device string
|
|
Cache time.Duration
|
|
}{
|
|
Cache: interval,
|
|
}
|
|
|
|
if err := util.DecodeOther(other, &cc); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if cc.Email == "" || cc.Password == "" {
|
|
return nil, api.ErrMissingCredentials
|
|
}
|
|
|
|
log := util.NewLogger("octopus-de").Redact(cc.Email, cc.Password)
|
|
|
|
api, err := octopuskraken.NewAPI(log, octopuskraken.BaseURI, cc.Email, cc.Password)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
v := &OctopusDe{
|
|
embed: &cc.embed,
|
|
API: api,
|
|
account: cc.AccountNumber,
|
|
device: cc.Device,
|
|
}
|
|
|
|
v.dataG = util.Cached(v.status, cc.Cache)
|
|
|
|
return v, nil
|
|
}
|
|
|
|
// status fetches the live state of the configured device, resolving the account
|
|
// and matching device on first use.
|
|
func (v *OctopusDe) status() (octopuskraken.Device, error) {
|
|
account, err := v.Account(v.account)
|
|
if err != nil {
|
|
return octopuskraken.Device{}, err
|
|
}
|
|
v.account = account
|
|
|
|
devices, err := v.Devices(account)
|
|
if err != nil {
|
|
return octopuskraken.Device{}, err
|
|
}
|
|
|
|
for _, d := range devices {
|
|
// match the configured device by id or name, or take the first one
|
|
if v.deviceID != "" {
|
|
if d.ID == v.deviceID {
|
|
return d, nil
|
|
}
|
|
continue
|
|
}
|
|
if v.device == "" || strings.EqualFold(d.ID, v.device) || strings.EqualFold(d.Name, v.device) {
|
|
v.deviceID = d.ID
|
|
v.fromVehicle(d.Name, 0)
|
|
return d, nil
|
|
}
|
|
}
|
|
|
|
if v.device == "" {
|
|
return octopuskraken.Device{}, api.ErrNotAvailable
|
|
}
|
|
return octopuskraken.Device{}, fmt.Errorf("device not found: %s", v.device)
|
|
}
|
|
|
|
// Soc implements the api.Vehicle interface
|
|
func (v *OctopusDe) Soc() (float64, error) {
|
|
res, err := v.dataG()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
soc, ok := res.Soc()
|
|
if !ok {
|
|
return 0, api.ErrNotAvailable
|
|
}
|
|
|
|
return soc, nil
|
|
}
|
|
|
|
var _ api.SocLimiter = (*OctopusDe)(nil)
|
|
|
|
// GetLimitSoc implements the api.SocLimiter interface
|
|
func (v *OctopusDe) GetLimitSoc() (int64, error) {
|
|
res, err := v.dataG()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
soc, ok := res.TargetSoc()
|
|
if !ok {
|
|
return 0, api.ErrNotAvailable
|
|
}
|
|
|
|
return int64(soc), nil
|
|
}
|