Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nissan: add Ariya template using v2 api #13401

Merged
merged 2 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions templates/definition/vehicle/nissan-ariya.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
template: nissan-ariya
products:
- brand: Nissan
description:
generic: Ariya
params:
- preset: vehicle-base
- preset: vehicle-identify
render: |
type: nissan
version: v2
{{ include "vehicle-base" . }}
{{ include "vehicle-identify" . }}
8 changes: 5 additions & 3 deletions vehicle/nissan.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ func NewNissanFromConfig(other map[string]interface{}) (api.Vehicle, error) {
cc := struct {
embed `mapstructure:",squash"`
User, Password, VIN string
Version string
Expiry time.Duration
Cache time.Duration
}{
Expiry: expiry,
Cache: interval,
Version: "v1", // battery api version: v2 for Ariya
Expiry: expiry,
Cache: interval,
}

if err := util.DecodeOther(other, &cc); err != nil {
Expand Down Expand Up @@ -64,7 +66,7 @@ func NewNissanFromConfig(other map[string]interface{}) (api.Vehicle, error) {
cc.VIN, err = ensureVehicle(cc.VIN, api.Vehicles)

if err == nil {
v.Provider = nissan.NewProvider(api, cc.VIN, cc.Expiry, cc.Cache)
v.Provider = nissan.NewProvider(api, cc.VIN, cc.Version, cc.Expiry, cc.Cache)
}

return v, err
Expand Down
6 changes: 3 additions & 3 deletions vehicle/nissan/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ func (v *API) Vehicles() ([]string, error) {
return vehicles, err
}

// Battery provides battery api response
func (v *API) BatteryStatus(vin string) (StatusResponse, error) {
uri := fmt.Sprintf("%s/v1/cars/%s/battery-status", CarAdapterBaseURL, vin)
// BatteryStatus provides battery api response
func (v *API) BatteryStatus(vin, version string) (StatusResponse, error) {
uri := fmt.Sprintf("%s/%s/cars/%s/battery-status", CarAdapterBaseURL, version, vin)

var res StatusResponse
err := v.GetJSON(uri, &res)
Expand Down
36 changes: 17 additions & 19 deletions vehicle/nissan/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,29 +64,27 @@ func (v *Identity) Login(user, password string) error {
}

// https://github.com/Tobiaswk/dartnissanconnect/commit/7d28dd5461aaed3e46b5be0c9fd58887e1e0cd0b
if err == nil {
err = api.ErrNotAvailable // not nil
for attempt := 1; attempt <= 10 && err != nil; attempt++ {
req, err = request.New(http.MethodPost, uri, request.MarshalJSON(res), map[string]string{
"Accept-Api-Version": APIVersion,
"X-Username": "anonymous",
"X-Password": "anonymous",
"Content-type": "application/json",
"Accept": "application/json",
})
if err == nil {
if err = v.DoJSON(req, &nToken); err != nil && !nToken.SessionExpired() {
break
}
err = api.ErrNotAvailable // not nil
for attempt := 1; attempt <= 10 && err != nil; attempt++ {
req, err = request.New(http.MethodPost, uri, request.MarshalJSON(res), map[string]string{
"Accept-Api-Version": APIVersion,
"X-Username": "anonymous",
"X-Password": "anonymous",
"Content-type": "application/json",
"Accept": "application/json",
})
if err == nil {
if err = v.DoJSON(req, &nToken); err != nil && !nToken.SessionExpired() {
break
}
}
}

if errT := nToken.Error(); err != nil && errT != nil {
err = errT
}

realm = strings.Trim(nToken.Realm, "/")
if errT := nToken.Error(); err != nil && errT != nil {
err = errT
}

realm = strings.Trim(nToken.Realm, "/")
}

if err == nil {
Expand Down
39 changes: 24 additions & 15 deletions vehicle/nissan/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type Provider struct {
}

// NewProvider returns a kamereon provider
func NewProvider(api *API, vin string, expiry, cache time.Duration) *Provider {
func NewProvider(api *API, vin, version string, expiry, cache time.Duration) *Provider {
impl := &Provider{
action: func(value Action) error {
_, err := api.ChargingAction(vin, value)
Expand All @@ -30,7 +30,7 @@ func NewProvider(api *API, vin string, expiry, cache time.Duration) *Provider {

impl.statusG = provider.Cached(func() (StatusResponse, error) {
return impl.status(
func() (StatusResponse, error) { return api.BatteryStatus(vin) },
func() (StatusResponse, error) { return api.BatteryStatus(vin, version) },
func() (ActionResponse, error) { return api.RefreshRequest(vin, "RefreshBatteryStatus") },
)
}, cache)
Expand All @@ -43,7 +43,8 @@ func (v *Provider) status(battery func() (StatusResponse, error), refresh func()

if err == nil {
// result valid?
if time.Since(res.Attributes.LastUpdateTime.Time) < v.expiry || res.Attributes.LastUpdateTime.IsZero() {
updated := res.Attributes.Updated()
if time.Since(updated) < v.expiry || updated.IsZero() {
v.refreshTime = time.Time{}
return res, err
}
Expand Down Expand Up @@ -116,33 +117,41 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
if err != nil {
return 0, err
}

if err == nil {
if res.Attributes.RangeHvacOff == nil {
return 0, api.ErrNotAvailable
}

if res.Attributes.RangeHvacOff != nil {
return int64(*res.Attributes.RangeHvacOff), nil
}

return 0, err
// v2
if res.Attributes.BatteryAutonomy != nil {
return int64(*res.Attributes.BatteryAutonomy), nil
}

return 0, api.ErrNotAvailable
}

var _ api.VehicleFinishTimer = (*Provider)(nil)

// FinishTime implements the api.VehicleFinishTimer interface
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.statusG()
if err != nil {
return time.Time{}, err
}

if err == nil {
if res.Attributes.RemainingTime == nil {
return time.Time{}, api.ErrNotAvailable
}
if res.Attributes.RemainingTime != nil {
minutes := time.Duration(*res.Attributes.RemainingTime) * time.Minute

return res.Attributes.LastUpdateTime.Time.Add(time.Duration(*res.Attributes.RemainingTime) * time.Minute), err
updated := res.Attributes.Updated()
if !updated.IsZero() {
return updated.Add(minutes), nil
}
}

return time.Time{}, err
return time.Time{}, api.ErrNotAvailable
}

var _ api.ChargeController = (*Provider)(nil)
Expand Down
38 changes: 26 additions & 12 deletions vehicle/nissan/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,32 @@ type StatusResponse struct {
}

type Attributes struct {
ChargeStatus float32 `json:"chargeStatus"`
RangeHvacOff *int `json:"rangeHvacOff"`
BatteryLevel int `json:"batteryLevel"`
BatteryCapacity int `json:"batteryCapacity"`
BatteryTemperature int `json:"batteryTemperature"`
PlugStatus int `json:"plugStatus"`
LastUpdateTime Timestamp `json:"lastUpdateTime"`
ChargePower int `json:"chargePower"`
RemainingTime *int `json:"chargingRemainingTime"`
RemainingToFullFast int `json:"timeRequiredToFullFast"`
RemainingToFullNormal int `json:"timeRequiredToFullNormal"`
RemainingToFullSlow int `json:"timeRequiredToFullSlow"`
ChargeStatus float32 `json:"chargeStatus"`
RangeHvacOff *int `json:"rangeHvacOff"`
BatteryLevel int `json:"batteryLevel"`
BatteryCapacity int `json:"batteryCapacity"`
BatteryTemperature int `json:"batteryTemperature"`
PlugStatus int `json:"plugStatus"`
LastUpdateTime *Timestamp `json:"lastUpdateTime"`
ChargePower int `json:"chargePower"`
RemainingTime *int `json:"chargingRemainingTime"`
RemainingToFullFast int `json:"timeRequiredToFullFast"`
RemainingToFullNormal int `json:"timeRequiredToFullNormal"`
RemainingToFullSlow int `json:"timeRequiredToFullSlow"`
// v2
Timestamp *time.Time `json:"timestamp"`
BatteryAutonomy *int `json:"batteryAutonomy"`
}

func (a *Attributes) Updated() time.Time {
if a.LastUpdateTime != nil {
// v1
return a.LastUpdateTime.Time
} else if a.Timestamp != nil {
// v2
return *a.Timestamp
}
return time.Time{}
}

type ActionResponse struct {
Expand Down