Require Go 1.18 and use generics (#2116)

This commit is contained in:
andig 2022-04-02 12:46:42 +02:00
parent cf3b6fb2a2
commit 1bfd0f6355
63 changed files with 467 additions and 788 deletions

View file

@ -42,7 +42,7 @@ You'll find everything you need in our [documentation](https://docs.evcc.io/) (G
## Build
To build EVCC from source, [Go][2] 1.17 and [Node][3] 16 are required:
To build EVCC from source, [Go][2] 1.18 and [Node][3] 16 are required:
```sh
make

View file

@ -33,7 +33,7 @@ import (
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/sponsor"
"github.com/philippseith/signalr"
"github.com/thoas/go-funk"
"github.com/samber/lo"
"golang.org/x/oauth2"
)
@ -109,7 +109,7 @@ func NewEasee(user, password, charger string) (*Easee, error) {
}
if len(chargers) != 1 {
return c, fmt.Errorf("cannot determine charger id, found: %v", funk.Map(chargers, func(c easee.Charger) string { return c.ID }))
return c, fmt.Errorf("cannot determine charger id, found: %v", lo.Map(chargers, func(c easee.Charger, _ int) string { return c.ID }))
}
c.charger = chargers[0].ID

View file

@ -5,7 +5,7 @@ import (
"strings"
"github.com/philippseith/signalr"
"github.com/thoas/go-funk"
"golang.org/x/exp/slices"
)
// Logger is a simple logger interface
@ -34,7 +34,7 @@ func (l *logger) Log(keyVals ...interface{}) error {
}
if i%2 == 0 {
if funk.Contains(skipped, v) {
if slices.Contains(skipped, v.(string)) {
skip = true
continue
}

View file

@ -1,5 +1,4 @@
//go:build !linux
// +build !linux
package charger

View file

@ -5,7 +5,7 @@ import (
"github.com/evcc-io/evcc/util/templates"
"github.com/evcc-io/evcc/util/test"
"github.com/thoas/go-funk"
"golang.org/x/exp/slices"
)
var acceptable = []string{
@ -44,7 +44,7 @@ func TestTemplates(t *testing.T) {
if values[templates.ParamModbus] != nil {
modbusChoices := tmpl.ModbusChoices()
// we only test one modbus setup
if funk.ContainsString(modbusChoices, templates.ModbusChoiceTCPIP) {
if slices.Contains(modbusChoices, templates.ModbusChoiceTCPIP) {
values[templates.ModbusKeyTCPIP] = true
} else {
values[templates.ModbusKeyRS485TCPIP] = true

View file

@ -10,8 +10,8 @@ import (
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/sponsor"
"github.com/evcc-io/evcc/util/templates"
"github.com/thoas/go-funk"
stripmd "github.com/writeas/go-strip-markdown"
"golang.org/x/exp/slices"
"gopkg.in/yaml.v3"
)
@ -128,7 +128,7 @@ func (c *CmdConfigure) processDeviceValues(values map[string]interface{}, templa
}
func (c *CmdConfigure) processDeviceCapabilities(capabilitites []string) {
if funk.ContainsString(capabilitites, templates.CapabilitySMAHems) {
if slices.Contains(capabilitites, templates.CapabilitySMAHems) {
c.capabilitySMAHems = true
}
}
@ -148,14 +148,14 @@ func (c *CmdConfigure) processDeviceRequirements(templateItem templates.Template
}
// check if sponsorship is required
if funk.ContainsString(templateItem.Requirements.EVCC, templates.RequirementSponsorship) && c.configuration.config.SponsorToken == "" {
if slices.Contains(templateItem.Requirements.EVCC, templates.RequirementSponsorship) && c.configuration.config.SponsorToken == "" {
if err := c.askSponsortoken(true, false); err != nil {
return err
}
}
// check if we need to setup an MQTT broker
if funk.ContainsString(templateItem.Requirements.EVCC, templates.RequirementMQTT) {
if slices.Contains(templateItem.Requirements.EVCC, templates.RequirementMQTT) {
if c.configuration.config.MQTT == "" {
mqttConfig, err := c.configureMQTT(templateItem)
if err != nil {
@ -172,7 +172,7 @@ func (c *CmdConfigure) processDeviceRequirements(templateItem templates.Template
}
// check if we need to setup an EEBUS HEMS
if funk.ContainsString(templateItem.Requirements.EVCC, templates.RequirementEEBUS) {
if slices.Contains(templateItem.Requirements.EVCC, templates.RequirementEEBUS) {
if c.configuration.config.EEBUS == "" {
fmt.Println()
fmt.Println("-- EEBUS -----------------------------------")
@ -219,7 +219,7 @@ func (c *CmdConfigure) processParamRequirements(param templates.Param) error {
}
// check if sponsorship is required
if funk.ContainsString(param.Requirements.EVCC, templates.RequirementSponsorship) && c.configuration.config.SponsorToken == "" {
if slices.Contains(param.Requirements.EVCC, templates.RequirementSponsorship) && c.configuration.config.SponsorToken == "" {
if err := c.askSponsortoken(true, true); err != nil {
return err
}
@ -384,7 +384,7 @@ func (c *CmdConfigure) fetchElements(deviceCategory DeviceCategory) []templates.
func (c *CmdConfigure) paramChoiceContains(params []templates.Param, name, filter string) bool {
choices := c.paramChoiceValues(params, name)
return funk.ContainsString(choices, filter)
return slices.Contains(choices, filter)
}
// paramChoiceValues provides a list of possible values for a param choice
@ -533,7 +533,7 @@ func (c *CmdConfigure) processInputConfig(param templates.Param) string {
}
help := param.Help.String(c.lang)
if funk.ContainsString(param.Requirements.EVCC, templates.RequirementSponsorship) {
if slices.Contains(param.Requirements.EVCC, templates.RequirementSponsorship) {
help = fmt.Sprintf("%s\n\n%s", help, c.localizedString("Requirements_Sponsorship_Feature_Title", nil))
}

View file

@ -16,7 +16,7 @@ import (
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/templates"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/thoas/go-funk"
"golang.org/x/exp/slices"
"golang.org/x/text/language"
)
@ -320,7 +320,7 @@ func (c *CmdConfigure) configureLoadpoints() {
}
var minValue int = 6
if funk.ContainsString(capabilities, templates.CapabilityISO151182) {
if slices.Contains(capabilities, templates.CapabilityISO151182) {
minValue = 2
}

View file

@ -11,8 +11,8 @@ import (
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util/templates"
"github.com/thoas/go-funk"
stripmd "github.com/writeas/go-strip-markdown"
"golang.org/x/exp/slices"
)
// surveyAskOne asks the user for input
@ -49,7 +49,7 @@ func (c *CmdConfigure) askSelection(message string, items []string) (string, int
var selection string
err := c.surveyAskOne(prompt, &selection)
return selection, funk.IndexOf(items, selection), err
return selection, slices.Index(items, selection), err
}
// selectItem selects item from list
@ -153,11 +153,11 @@ func (c *CmdConfigure) askValue(q question) string {
validate := func(val interface{}) error {
value := val.(string)
if q.invalidValues != nil && funk.ContainsString(q.invalidValues, value) {
if q.invalidValues != nil && slices.Contains(q.invalidValues, value) {
return errors.New(c.localizedString("ValueError_Used", nil))
}
if q.validValues != nil && !funk.ContainsString(q.validValues, value) {
if q.validValues != nil && !slices.Contains(q.validValues, value) {
return errors.New(c.localizedString("ValueError_Invalid", nil))
}

View file

@ -1,5 +1,4 @@
//go:build !gokrazy
// +build !gokrazy
package cmd

View file

@ -1,5 +1,4 @@
//go:build gokrazy
// +build gokrazy
package cmd

View file

@ -1,5 +1,4 @@
//go:build gokrazy
// +build gokrazy
package cmd

View file

@ -1,5 +1,4 @@
//go:build !windows
// +build !windows
package cmd

View file

@ -6,9 +6,10 @@ import (
"github.com/evcc-io/evcc/server"
"github.com/evcc-io/evcc/util"
"github.com/samber/lo"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/thoas/go-funk"
"golang.org/x/exp/slices"
"golang.org/x/oauth2"
)
@ -37,15 +38,19 @@ func runToken(cmd *cobra.Command, args []string) {
if len(conf.Vehicles) == 1 {
vehicleConf = conf.Vehicles[0]
} else if len(args) == 1 {
vehicleConf = funk.Find(conf.Vehicles, func(v qualifiedConfig) bool {
idx := slices.IndexFunc(conf.Vehicles, func(v qualifiedConfig) bool {
return strings.EqualFold(v.Name, args[0])
}).(qualifiedConfig)
})
if idx >= 0 {
vehicleConf = conf.Vehicles[idx]
}
}
if vehicleConf.Name == "" {
vehicles := funk.Map(conf.Vehicles, func(v qualifiedConfig) string {
vehicles := lo.Map(conf.Vehicles, func(v qualifiedConfig, _ int) string {
return v.Name
}).([]string)
})
log.FATAL.Fatalf("vehicle not found, have %v", vehicles)
}

View file

@ -16,7 +16,7 @@ import (
"github.com/evcc-io/evcc/provider"
"github.com/evcc-io/evcc/push"
"github.com/evcc-io/evcc/util"
"github.com/thoas/go-funk"
"golang.org/x/exp/slices"
evbus "github.com/asaskevich/EventBus"
"github.com/avast/retry-go/v3"
@ -760,7 +760,7 @@ func (lp *LoadPoint) identifyVehicle() {
func (lp *LoadPoint) selectVehicleByID(id string) api.Vehicle {
// find exact match
for _, vehicle := range lp.vehicles {
if funk.ContainsString(vehicle.Identifiers(), id) {
if slices.Contains(vehicle.Identifiers(), id) {
return vehicle
}
}

View file

@ -39,7 +39,7 @@ func Work(log *util.Logger, num int, hosts []string) []tasks.Result {
// log.INFO.Println(
// "\n" +
// strings.Join(
// funk.Map(taskList.tasks, func(t tasks.Task) string {
// lo.Map(taskList.tasks, func(t tasks.Task) string {
// return fmt.Sprintf("task: %s\ttype: %s\tdepends: %s\n", t.ID, t.Type, t.Depends)
// }).([]string),
// "",

10
go.mod
View file

@ -1,6 +1,6 @@
module github.com/evcc-io/evcc
go 1.17
go 1.18
require (
github.com/AlecAivazis/survey/v2 v2.3.2
@ -52,6 +52,7 @@ require (
github.com/lorenzodonini/ocpp-go v0.15.0
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
github.com/manifoldco/promptui v0.9.0
github.com/mergermarket/go-pkcs7 v0.0.0-20170926155232-153b18ea13c9
github.com/mitchellh/mapstructure v1.4.3
github.com/mlnoga/rct v0.1.2-0.20220320164346-9f2daa4d6734
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a
@ -62,6 +63,7 @@ require (
github.com/philippseith/signalr v0.5.3-0.20211205201131-d57b5a34379a
github.com/prometheus/client_golang v1.11.0
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
github.com/samber/lo v1.11.0
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/spf13/cobra v1.4.0
@ -69,12 +71,12 @@ require (
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.10.2-0.20220212101550-5986bd9c0c19
github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942
github.com/thoas/go-funk v0.9.1
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c
github.com/volkszaehler/mbmd v0.0.0-20220329124309-22084e041a33
github.com/writeas/go-strip-markdown v2.0.1+incompatible
gitlab.com/bboehmke/sunny v0.15.1-0.20211022160056-2fba1c86ade6
golang.org/x/net v0.0.0-20220325170049-de3da57026de
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a
golang.org/x/text v0.3.7
google.golang.org/api v0.73.0
@ -122,7 +124,6 @@ require (
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mergermarket/go-pkcs7 v0.0.0-20170926155232-153b18ea13c9
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/miekg/dns v1.1.45 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
@ -146,6 +147,7 @@ require (
github.com/spf13/cast v1.4.1 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/teivah/onecontext v1.3.0 // indirect
github.com/thoas/go-funk v0.9.1
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
go.opencensus.io v0.23.0 // indirect

17
go.sum
View file

@ -29,7 +29,6 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y=
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
@ -123,7 +122,6 @@ github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuD
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
@ -342,7 +340,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
@ -459,7 +456,6 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb-client-go v1.4.0 h1:+KavOkwhLClHFfYcJMHHnTL5CZQhXJzOm5IKHI9BqJk=
github.com/influxdata/influxdb-client-go v1.4.0/go.mod h1:S+oZsPivqbcP1S9ur+T+QqXvrYS3NCZeMQtBoH4D1dw=
github.com/influxdata/influxdb-client-go/v2 v2.6.0 h1:bIOaGTgvvv1Na2hG+nIvqyv7PK2UiU2WrJN1ck1ykyM=
github.com/influxdata/influxdb-client-go/v2 v2.6.0/go.mod h1:Y/0W1+TZir7ypoQZYd2IrnVOKB3Tq6oegAQeSVN/+EU=
@ -729,6 +725,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM=
github.com/samber/lo v1.11.0 h1:JfeYozXL1xfkhRUFOfH13ociyeiLSC/GRJjGKI668xM=
github.com/samber/lo v1.11.0/go.mod h1:2I7tgIv8Q1SG2xEIkRq0F2i2zgxVpnyPOP0d3Gj2r+A=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
@ -756,7 +754,6 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.8.1/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
@ -832,7 +829,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
gitlab.com/bboehmke/sunny v0.15.1-0.20211022160056-2fba1c86ade6 h1:73pM5aQqFkQfmXyGf3+xeWWg98J03xtDIkA5TE37ERU=
gitlab.com/bboehmke/sunny v0.15.1-0.20211022160056-2fba1c86ade6/go.mod h1:F5AIuL7kYteSJFR5E+YEocxIdpyCXmtDciFmMQVjP88=
go.coder.com/go-tools v0.0.0-20190317003359-0c6a35b74a16/go.mod h1:iKV5yK9t+J5nG9O3uF6KYdPEz3dyfMyB15MN1rbQ8Qw=
@ -891,6 +887,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 h1:s/+U+w0teGzcoH2mdIlFQ6KfVKGaYpgyGdUefZrn9TU=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -980,14 +978,11 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1106,7 +1101,6 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211112164355-7580c6e521dc/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1396,7 +1390,6 @@ gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWd
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ=

View file

@ -10,7 +10,7 @@ import (
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/transport"
"github.com/thoas/go-funk"
"github.com/samber/lo"
)
func init() {
@ -18,7 +18,7 @@ func init() {
}
type Discovergy struct {
dataG func() (interface{}, error)
dataG func() (discovergy.Reading, error)
scale float64
}
@ -63,17 +63,17 @@ func NewDiscovergyFromConfig(other map[string]interface{}) (api.Meter, error) {
}
if meterID == "" {
return nil, fmt.Errorf("could not determine meter id: %v", funk.Map(meters, func(m discovergy.Meter) string {
return nil, fmt.Errorf("could not determine meter id: %v", lo.Map(meters, func(m discovergy.Meter, _ int) string {
return m.FullSerialNumber
}))
}
dataG := provider.NewCached(func() (interface{}, error) {
dataG := provider.Cached(func() (discovergy.Reading, error) {
var res discovergy.Reading
uri := fmt.Sprintf("%s/last_reading?meterId=%s", discovergy.API, meterID)
err := client.GetJSON(uri, &res)
return res, err
}, cc.Cache).InterfaceGetter()
}, cc.Cache)
m := &Discovergy{
dataG: dataG,
@ -91,18 +91,12 @@ var _ api.Meter = (*Discovergy)(nil)
func (m *Discovergy) CurrentPower() (float64, error) {
res, err := m.dataG()
if res, ok := res.(discovergy.Reading); err == nil && ok {
return m.scale * float64(res.Values.Power) / 1e3, nil
}
return 0, err
return m.scale * float64(res.Values.Power) / 1e3, err
}
var _ api.MeterEnergy = (*Discovergy)(nil)
func (m *Discovergy) TotalEnergy() (float64, error) {
res, err := m.dataG()
if res, ok := res.(discovergy.Reading); err == nil && ok {
return float64(res.Values.Energy) / 1e10, nil
}
return 0, err
return float64(res.Values.Energy) / 1e10, err
}

View file

@ -41,10 +41,10 @@ type EssData struct {
type Com struct {
*request.Helper
uri string // URI address of the LG ESS inverter - e.g. "https://192.168.1.28"
password string // registration number of the LG ESS Inverter - e.g. "DE2001..."
authKey string // auth_key returned during login and renewed with new login after expiration
cachedData func() (interface{}, error)
uri string // URI address of the LG ESS inverter - e.g. "https://192.168.1.28"
password string // registration number of the LG ESS Inverter - e.g. "DE2001..."
authKey string // auth_key returned during login and renewed with new login after expiration
dataG func() (EssData, error)
}
var (
@ -70,9 +70,7 @@ func GetInstance(uri, password string, cache time.Duration) (*Com, error) {
// caches the data access for the "cache" time duration
// sends a new request to the pcs if the cache is expired and Data() requested
instance.cachedData = provider.NewCached(func() (interface{}, error) {
return instance.refreshData()
}, cache).InterfaceGetter()
instance.dataG = provider.Cached(instance.refreshData, cache)
// do first login if no authKey exists and uri and password exist
if instance.authKey == "" && instance.uri != "" && instance.password != "" {
@ -127,8 +125,7 @@ func (m *Com) Login() error {
// Data gives the data read from the pcs.
func (m *Com) Data() (EssData, error) {
res, err := m.cachedData()
return res.(EssData), err
return m.dataG()
}
// refreshData reads data from lgess pcs. Tries to re-login if "405" auth_key expired is returned

View file

@ -5,7 +5,7 @@ import (
"github.com/evcc-io/evcc/util/templates"
"github.com/evcc-io/evcc/util/test"
"github.com/thoas/go-funk"
"golang.org/x/exp/slices"
)
var acceptable = []string{
@ -42,7 +42,7 @@ func TestTemplates(t *testing.T) {
if values[templates.ParamModbus] != nil {
modbusChoices := tmpl.ModbusChoices()
// we only test one modbus setup
if funk.ContainsString(modbusChoices, templates.ModbusChoiceTCPIP) {
if slices.Contains(modbusChoices, templates.ModbusChoiceTCPIP) {
values[templates.ModbusKeyTCPIP] = true
} else {
values[templates.ModbusKeyRS485TCPIP] = true

View file

@ -22,176 +22,60 @@ func ResetCached() {
bus.Publish(reset)
}
// Cached wraps a getter with a cache
type Cached struct {
// cached wraps a getter with a cache
type cached[T any] struct {
mux sync.Mutex
clock clock.Clock
updated time.Time
cache time.Duration
getter interface{}
val interface{}
g func() (T, error)
val T
err error
}
// NewCached wraps a getter with a cache
func NewCached(getter interface{}, cache time.Duration) *Cached {
c := &Cached{
clock: clock.New(),
getter: getter,
cache: cache,
}
_ = bus.Subscribe(reset, c.reset)
return c
// Cached wraps a getter with a cache
func Cached[T any](g func() (T, error), cache time.Duration) func() (T, error) {
c := ResettableCached(g, cache)
_ = bus.Subscribe(reset, c.Reset)
return c.Get
}
func (c *Cached) reset() {
// Cachable is the interface for a resettable cache
type Cachable[T any] interface {
Get() (T, error)
Reset()
}
var _ Cachable[int64] = (*cached[int64])(nil)
// ResettableCached wraps a getter with a cache. It returns a `Cachable`.
// Instead of the cached getter, the `Get()` and `Reset()` methods are exposed.
func ResettableCached[T any](g func() (T, error), cache time.Duration) *cached[T] {
return &cached[T]{
clock: clock.New(),
cache: cache,
g: g,
}
}
func (c *cached[T]) Get() (T, error) {
c.mux.Lock()
defer c.mux.Unlock()
if c.mustUpdate() {
c.val, c.err = c.g()
c.updated = c.clock.Now()
}
return c.val, c.err
}
func (c *cached[T]) Reset() {
c.mux.Lock()
c.updated = time.Time{}
c.mux.Unlock()
}
func (c *Cached) mustUpdate() bool {
func (c *cached[T]) mustUpdate() bool {
return c.clock.Since(c.updated) > c.cache || errors.Is(c.err, api.ErrMustRetry)
}
// FloatGetter gets float value
func (c *Cached) FloatGetter() func() (float64, error) {
g, ok := c.getter.(func() (float64, error))
if !ok {
log.FATAL.Fatalf("invalid type: %T", c.getter)
}
return func() (float64, error) {
c.mux.Lock()
defer c.mux.Unlock()
if c.mustUpdate() {
c.val, c.err = g()
c.updated = c.clock.Now()
}
return c.val.(float64), c.err
}
}
// IntGetter gets int value
func (c *Cached) IntGetter() func() (int64, error) {
g, ok := c.getter.(func() (int64, error))
if !ok {
log.FATAL.Fatalf("invalid type: %T", c.getter)
}
return func() (int64, error) {
c.mux.Lock()
defer c.mux.Unlock()
if c.mustUpdate() {
c.val, c.err = g()
c.updated = c.clock.Now()
}
return c.val.(int64), c.err
}
}
// StringGetter gets string value
func (c *Cached) StringGetter() func() (string, error) {
g, ok := c.getter.(func() (string, error))
if !ok {
log.FATAL.Fatalf("invalid type: %T", c.getter)
}
return func() (string, error) {
c.mux.Lock()
defer c.mux.Unlock()
if c.mustUpdate() {
c.val, c.err = g()
c.updated = c.clock.Now()
}
return c.val.(string), c.err
}
}
// BoolGetter gets bool value
func (c *Cached) BoolGetter() func() (bool, error) {
g, ok := c.getter.(func() (bool, error))
if !ok {
log.FATAL.Fatalf("invalid type: %T", c.getter)
}
return func() (bool, error) {
c.mux.Lock()
defer c.mux.Unlock()
if c.mustUpdate() {
c.val, c.err = g()
c.updated = c.clock.Now()
}
return c.val.(bool), c.err
}
}
// DurationGetter gets time.Duration value
func (c *Cached) DurationGetter() func() (time.Duration, error) {
g, ok := c.getter.(func() (time.Duration, error))
if !ok {
log.FATAL.Fatalf("invalid type: %T", c.getter)
}
return func() (time.Duration, error) {
c.mux.Lock()
defer c.mux.Unlock()
if c.mustUpdate() {
c.val, c.err = g()
c.updated = c.clock.Now()
}
return c.val.(time.Duration), c.err
}
}
// TimeGetter gets time.Time value
func (c *Cached) TimeGetter() func() (time.Time, error) {
g, ok := c.getter.(func() (time.Time, error))
if !ok {
log.FATAL.Fatalf("invalid type: %T", c.getter)
}
return func() (time.Time, error) {
c.mux.Lock()
defer c.mux.Unlock()
if c.mustUpdate() {
c.val, c.err = g()
c.updated = c.clock.Now()
}
return c.val.(time.Time), c.err
}
}
// InterfaceGetter gets interface value
func (c *Cached) InterfaceGetter() func() (interface{}, error) {
g, ok := c.getter.(func() (interface{}, error))
if !ok {
log.FATAL.Fatalf("invalid type: %T", c.getter)
}
return func() (interface{}, error) {
c.mux.Lock()
defer c.mux.Unlock()
if c.mustUpdate() {
c.val, c.err = g()
c.updated = c.clock.Now()
}
return c.val, c.err
}
}

View file

@ -27,16 +27,15 @@ func TestCachedGetter(t *testing.T) {
}
duration := time.Second
c := NewCached(g, duration)
c := ResettableCached(g, duration)
clock := clock.NewMock()
c.clock = clock
getter := c.FloatGetter()
expect := func(s struct {
f float64
e error
}) {
f, e := getter()
f, e := c.Get()
if f != s.f || e != s.e {
t.Errorf("unexpected cache value: %f, %v\n", f, e)
}
@ -59,12 +58,12 @@ func TestCacheReset(t *testing.T) {
return i, nil
}
c := NewCached(g, 10*time.Minute)
c := ResettableCached(g, 10*time.Minute)
clock := clock.NewMock()
c.clock = clock
test := func(exp int64) {
v, _ := c.IntGetter()()
v, _ := c.Get()
if exp != v {
t.Errorf("expected %d, got %d", exp, v)
}
@ -72,7 +71,7 @@ func TestCacheReset(t *testing.T) {
test(1)
test(1)
ResetCached()
c.Reset()
test(2)
test(2)
clock.Add(10*time.Minute + 1)

View file

@ -1,5 +1,4 @@
//go:build !release
// +build !release
package server

View file

@ -1,5 +1,4 @@
//go:build !windows
// +build !windows
package server

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package server

View file

@ -1,5 +1,4 @@
//go:build gokrazy
// +build gokrazy
package updater

View file

@ -1,5 +1,4 @@
//go:build !gokrazy
// +build !gokrazy
package updater

View file

@ -1,5 +1,4 @@
//go:build gokrazy
// +build gokrazy
package updater

View file

@ -1,5 +1,4 @@
//go:build tools
// +build tools
package main

View file

@ -1,8 +1,8 @@
// Approach from https://github.com/theckman/goconstraint/
// Package goversion should only be used as a blank import. If imported, it
// will only compile if the Go runtime version is >= 1.17.
// will only compile if the Go runtime version is >= 1.18.
package goversion
// This will fail to compile if the Go runtime version isn't >= 1.17.
var _ = __EVCC_REQUIRES_GO_VERSION_1_17__
// This will fail to compile if the Go runtime version isn't >= 1.18.
var _ = __EVCC_REQUIRES_GO_VERSION_1_18__

View file

@ -1,6 +1,5 @@
//go:build go1.17
// +build go1.17
//go:build go1.18
package goversion
const __EVCC_REQUIRES_GO_VERSION_1_17__ = uint8(0)
const __EVCC_REQUIRES_GO_VERSION_1_18__ = uint8(0)

View file

@ -9,7 +9,7 @@ import (
"github.com/Masterminds/sprig/v3"
"github.com/evcc-io/evcc/util"
"github.com/thoas/go-funk"
"golang.org/x/exp/slices"
)
// Template describes is a proxy device for use with cli and automated testing
@ -27,7 +27,7 @@ type Template struct {
// UpdateParamWithDefaults adds default values to specific param name entries
func (t *Template) UpdateParamsWithDefaults() error {
for i, p := range t.Params {
if p.ValueType == "" || (p.ValueType != "" && !funk.ContainsString(ValidParamValueTypes, p.ValueType)) {
if p.ValueType == "" || (p.ValueType != "" && !slices.Contains(ValidParamValueTypes, p.ValueType)) {
t.Params[i].ValueType = ParamValueTypeString
}
@ -42,13 +42,13 @@ func (t *Template) UpdateParamsWithDefaults() error {
// validate the template (only rudimentary for now)
func (t *Template) Validate() error {
for _, c := range t.Capabilities {
if !funk.ContainsString(ValidCapabilities, c) {
if !slices.Contains(ValidCapabilities, c) {
return fmt.Errorf("invalid capability '%s' in template %s", c, t.Template)
}
}
for _, r := range t.Requirements.EVCC {
if !funk.ContainsString(ValidRequirements, r) {
if !slices.Contains(ValidRequirements, r) {
return fmt.Errorf("invalid requirement '%s' in template %s", r, t.Template)
}
}
@ -57,24 +57,24 @@ func (t *Template) Validate() error {
switch p.Name {
case ParamUsage:
for _, c := range p.Choice {
if !funk.ContainsString(ValidUsageChoices, c) {
if !slices.Contains(ValidUsageChoices, c) {
return fmt.Errorf("invalid usage choice '%s' in template %s", c, t.Template)
}
}
case ParamModbus:
for _, c := range p.Choice {
if !funk.ContainsString(ValidModbusChoices, c) {
if !slices.Contains(ValidModbusChoices, c) {
return fmt.Errorf("invalid modbus choice '%s' in template %s", c, t.Template)
}
}
}
if p.ValueType != "" && !funk.ContainsString(ValidParamValueTypes, p.ValueType) {
if p.ValueType != "" && !slices.Contains(ValidParamValueTypes, p.ValueType) {
return fmt.Errorf("invalid value type '%s' in template %s", p.ValueType, t.Template)
}
for _, d := range p.Dependencies {
if !funk.ContainsString(ValidDependencies, d.Check) {
if !slices.Contains(ValidDependencies, d.Check) {
return fmt.Errorf("invalid dependency check '%s' in template %s", d.Check, t.Template)
}
}
@ -303,7 +303,7 @@ func (t *Template) RenderResult(renderMode string, other map[string]interface{})
for item, p := range values {
i, _ := t.ParamByName(item)
if i == -1 && !funk.ContainsString(predefinedTemplateProperties, item) {
if i == -1 && !slices.Contains(predefinedTemplateProperties, item) {
return nil, values, fmt.Errorf("invalid element 'name: %s'", item)
}

View file

@ -1,8 +1,6 @@
package vehicle
import (
"fmt"
"strings"
"time"
"github.com/evcc-io/evcc/api"
@ -77,27 +75,17 @@ func newBluelinkFromConfig(brand string, other map[string]interface{}, settings
api := bluelink.NewAPI(log, settings.URI, identity, cc.Cache)
vehicles, err := api.Vehicles()
_, vehicle, err := ensureVehicleWithFeature(
cc.VIN, api.Vehicles,
func(v bluelink.Vehicle) (string, bluelink.Vehicle) {
return v.VIN, v
},
)
if err != nil {
return nil, err
}
var vehicle bluelink.Vehicle
if cc.VIN == "" && len(vehicles) == 1 {
vehicle = vehicles[0]
log.DEBUG.Printf("found vehicle: %v", vehicle.VIN)
} else {
for _, v := range vehicles {
if v.VIN == strings.ToUpper(cc.VIN) {
vehicle = v
}
}
}
if len(vehicle.VIN) == 0 {
return nil, fmt.Errorf("cannot find vehicle: %v", vehicles)
}
v := &Bluelink{
embed: &cc.embed,
Provider: bluelink.NewProvider(api, vehicle.VehicleID, cc.Expiry, cc.Cache),

View file

@ -12,8 +12,8 @@ const refreshTimeout = 2 * time.Minute
// Provider implements the Kia/Hyundai bluelink api.
// Based on https://github.com/Hacksore/bluelinky.
type Provider struct {
statusG func() (interface{}, error)
statusLG func() (interface{}, error)
statusG func() (VehicleStatus, error)
statusLG func() (StatusLatestResponse, error)
refreshG func() (StatusResponse, error)
expiry time.Duration
refreshTime time.Time
@ -28,15 +28,15 @@ func NewProvider(api *API, vid string, expiry, cache time.Duration) *Provider {
expiry: expiry,
}
v.statusG = provider.NewCached(func() (interface{}, error) {
v.statusG = provider.Cached(func() (VehicleStatus, error) {
return v.status(
func() (StatusLatestResponse, error) { return api.StatusLatest(vid) },
)
}, cache).InterfaceGetter()
}, cache)
v.statusLG = provider.NewCached(func() (interface{}, error) {
v.statusLG = provider.Cached(func() (StatusLatestResponse, error) {
return api.StatusLatest(vid)
}, cache).InterfaceGetter()
}, cache)
return v
}
@ -97,7 +97,7 @@ var _ api.Battery = (*Provider)(nil)
func (v *Provider) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(VehicleStatus); err == nil && ok {
if err == nil {
return res.EvStatus.BatteryStatus, nil
}
@ -111,7 +111,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
res, err := v.statusG()
status := api.StatusNone
if res, ok := res.(VehicleStatus); err == nil && ok {
if err == nil {
status = api.StatusA
if res.EvStatus.BatteryPlugin > 0 {
status = api.StatusB
@ -130,7 +130,7 @@ var _ api.VehicleFinishTimer = (*Provider)(nil)
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.statusG()
if res, ok := res.(VehicleStatus); err == nil && ok {
if err == nil {
remaining := res.EvStatus.RemainTime2.Atc.Value
if remaining == 0 {
@ -150,7 +150,7 @@ var _ api.VehicleRange = (*Provider)(nil)
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(VehicleStatus); err == nil && ok {
if err == nil {
if dist := res.EvStatus.DrvDistance; len(dist) == 1 {
return int64(dist[0].RangeByFuel.EvModeRange.Value), nil
}
@ -166,10 +166,5 @@ var _ api.VehicleOdometer = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Odometer() (float64, error) {
res, err := v.statusLG()
if res, ok := res.(StatusLatestResponse); err == nil && ok {
return res.ResMsg.VehicleStatusInfo.Odometer.Value, nil
}
return 0, err
return res.ResMsg.VehicleStatusInfo.Odometer.Value, err
}

View file

@ -9,15 +9,15 @@ import (
// Provider implements the evcc vehicle api
type Provider struct {
statusG func() (interface{}, error)
statusG func() (VehicleStatus, error)
}
// NewProvider provides the evcc vehicle api provider
func NewProvider(api *API, vin string, cache time.Duration) *Provider {
impl := &Provider{
statusG: provider.NewCached(func() (interface{}, error) {
statusG: provider.Cached(func() (VehicleStatus, error) {
return api.Status(vin)
}, cache).InterfaceGetter(),
}, cache),
}
return impl
}
@ -27,7 +27,7 @@ var _ api.Battery = (*Provider)(nil)
// SoC implements the api.Vehicle interface
func (v *Provider) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(VehicleStatus); err == nil && ok {
if err == nil {
if cs := res.Properties.ChargingState; cs != nil {
return float64(cs.ChargePercentage), nil
}
@ -43,7 +43,7 @@ var _ api.ChargeState = (*Provider)(nil)
// Status implements the api.ChargeState interface
func (v *Provider) Status() (api.ChargeStatus, error) {
res, err := v.statusG()
if res, ok := res.(VehicleStatus); err == nil && ok {
if err == nil {
if cs := res.Properties.ChargingState; cs != nil {
status := api.StatusA // disconnected
@ -68,7 +68,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
// // FinishTime implements the api.VehicleFinishTimer interface
// func (v *Provider) FinishTime() (time.Time, error) {
// res, err := v.statusG()
// if res, ok := res.(StatusResponse); err == nil && ok {
// err == nil {
// ctr := res.VehicleStatus.ChargingTimeRemaining
// return time.Now().Add(time.Duration(ctr) * time.Minute), err
// }
@ -81,7 +81,7 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(VehicleStatus); err == nil && ok {
if err == nil {
if er := res.Properties.ElectricRange; er != nil {
return int64(er.Distance.Value), nil
}
@ -97,7 +97,7 @@ var _ api.VehicleOdometer = (*Provider)(nil)
// Odometer implements the api.VehicleOdometer interface
func (v *Provider) Odometer() (float64, error) {
res, err := v.statusG()
if res, ok := res.(VehicleStatus); err == nil && ok {
if err == nil {
if cm := res.Status.CurrentMileage; cm != nil {
return float64(cm.Mileage), nil
}

View file

@ -26,8 +26,8 @@ type CarWings struct {
*embed
user, password string
session *carwings.Session
statusG func() (interface{}, error)
climateG func() (interface{}, error)
statusG func() (carwings.BatteryStatus, error)
climateG func() (carwings.ClimateStatus, error)
refreshKey string
refreshTime time.Time
}
@ -91,13 +91,8 @@ func NewCarWingsFromConfig(other map[string]interface{}) (api.Vehicle, error) {
return nil, fmt.Errorf("login failed: %w", err)
}
v.statusG = provider.NewCached(func() (interface{}, error) {
return v.status()
}, cc.Cache).InterfaceGetter()
v.climateG = provider.NewCached(func() (interface{}, error) {
return v.session.ClimateControlStatus()
}, cc.Cache).InterfaceGetter()
v.statusG = provider.Cached(v.status, cc.Cache)
v.climateG = provider.Cached(v.session.ClimateControlStatus, cc.Cache)
return v, nil
}
@ -112,11 +107,11 @@ func (v *CarWings) connectIfRequired(err error) error {
return err
}
func (v *CarWings) status() (interface{}, error) {
func (v *CarWings) status() (carwings.BatteryStatus, error) {
// api result is stale
if v.refreshKey != "" {
if err := v.refreshResult(); err != nil {
return nil, err
return *new(carwings.BatteryStatus), err
}
}
@ -125,7 +120,7 @@ func (v *CarWings) status() (interface{}, error) {
if err == nil {
if elapsed := time.Since(bs.Timestamp); elapsed > carwingsStatusExpiry {
if err = v.refreshRequest(); err != nil {
return nil, err
return *new(carwings.BatteryStatus), err
}
err = api.ErrMustRetry
@ -182,7 +177,7 @@ func (v *CarWings) refreshRequest() (err error) {
// SoC implements the api.Vehicle interface
func (v *CarWings) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(carwings.BatteryStatus); err == nil && ok {
if err == nil {
return float64(res.StateOfCharge), nil
}
@ -196,7 +191,7 @@ func (v *CarWings) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.statusG()
if res, ok := res.(carwings.BatteryStatus); err == nil && ok {
if err == nil {
if res.PluginState == carwings.Connected {
status = api.StatusB // connected, not charging
}
@ -213,7 +208,7 @@ var _ api.VehicleRange = (*CarWings)(nil)
// Range implements the api.VehicleRange interface
func (v *CarWings) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(carwings.BatteryStatus); err == nil && ok {
if err == nil {
return int64(res.CruisingRangeACOn) / 1000, nil
}
@ -226,7 +221,7 @@ var _ api.VehicleClimater = (*CarWings)(nil)
func (v *CarWings) Climater() (active bool, outsideTemp float64, targetTemp float64, err error) {
res, err := v.climateG()
if res, ok := res.(carwings.ClimateStatus); err == nil && ok {
if err == nil {
active = res.Running
targetTemp = float64(res.Temperature)
outsideTemp = targetTemp

View file

@ -66,7 +66,7 @@ func NewCloudFromConfig(other map[string]interface{}) (api.Vehicle, error) {
err = v.prepareVehicle()
}
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).FloatGetter()
v.chargeStateG = provider.Cached(v.chargeState, cc.Cache)
return v, err
}

View file

@ -11,8 +11,8 @@ import (
const refreshTimeout = 2 * time.Minute
type Provider struct {
statusG func() (interface{}, error)
locationG func() (interface{}, error)
statusG func() (StatusResponse, error)
locationG func() (LocationResponse, error)
action func(action, cmd string) (ActionResponse, error)
expiry time.Duration
refreshTime time.Time
@ -20,12 +20,12 @@ type Provider struct {
func NewProvider(api *API, vin, pin string, expiry, cache time.Duration) *Provider {
impl := &Provider{
statusG: provider.NewCached(func() (interface{}, error) {
statusG: provider.Cached(func() (StatusResponse, error) {
return api.Status(vin)
}, cache).InterfaceGetter(),
locationG: provider.NewCached(func() (interface{}, error) {
}, cache),
locationG: provider.Cached(func() (LocationResponse, error) {
return api.Location(vin)
}, cache).InterfaceGetter(),
}, cache),
action: func(action, cmd string) (ActionResponse, error) {
return api.Action(vin, pin, action, cmd)
},
@ -34,11 +34,11 @@ func NewProvider(api *API, vin, pin string, expiry, cache time.Duration) *Provid
// use pin for refreshing
if pin != "" {
impl.statusG = provider.NewCached(func() (interface{}, error) {
impl.statusG = provider.Cached(func() (StatusResponse, error) {
return impl.status(
func() (StatusResponse, error) { return api.Status(vin) },
)
}, cache).InterfaceGetter()
}, cache)
}
return impl
@ -88,7 +88,7 @@ func (v *Provider) status(statusG func() (StatusResponse, error)) (StatusRespons
// SoC implements the api.Vehicle interface
func (v *Provider) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
if res.EvInfo == nil {
return 0, api.ErrNotAvailable
}
@ -104,7 +104,7 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
if res.EvInfo == nil {
return 0, api.ErrNotAvailable
}
@ -120,7 +120,7 @@ var _ api.VehicleOdometer = (*Provider)(nil)
// Odometer implements the api.VehicleOdometer interface
func (v *Provider) Odometer() (float64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
return float64(res.VehicleInfo.Odometer.Odometer.Value), nil
}
@ -134,7 +134,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
if res.EvInfo == nil {
return api.StatusNone, api.ErrNotAvailable
}
@ -155,7 +155,7 @@ var _ api.VehiclePosition = (*Provider)(nil)
// Position implements the api.VehiclePosition interface
func (v *Provider) Position() (float64, float64, error) {
res, err := v.locationG()
if res, ok := res.(LocationResponse); err == nil && ok {
if err == nil {
return res.Latitude, res.Longitude, nil
}

View file

@ -10,7 +10,7 @@ import (
const refreshTimeout = time.Minute
type Provider struct {
statusG func() (interface{}, error)
statusG func() (StatusResponse, error)
expiry time.Duration
refreshTime time.Time
refreshId string
@ -22,13 +22,13 @@ func NewProvider(api *API, vin string, expiry, cache time.Duration) *Provider {
expiry: expiry,
}
impl.statusG = provider.NewCached(func() (interface{}, error) {
impl.statusG = provider.Cached(func() (StatusResponse, error) {
return impl.status(
func() (StatusResponse, error) { return api.Status(vin) },
func(id string) (StatusResponse, error) { return api.RefreshResult(vin, id) },
func() (string, error) { return api.RefreshRequest(vin) },
)
}, cache).InterfaceGetter()
}, cache)
impl.wakeup = func() error { return api.WakeUp(vin) }
@ -78,7 +78,7 @@ var _ api.Battery = (*Provider)(nil)
// SoC implements the api.Battery interface
func (v *Provider) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
return res.VehicleStatus.BatteryFillLevel.Value, nil
}
@ -90,7 +90,7 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
return int64(res.VehicleStatus.ElVehDTE.Value), nil
}
@ -104,7 +104,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
if res.VehicleStatus.PlugStatus.Value == 1 {
status = api.StatusB // connected, not charging
}
@ -121,11 +121,7 @@ var _ api.VehicleOdometer = (*Provider)(nil)
// Odometer implements the api.VehicleOdometer interface
func (v *Provider) Odometer() (float64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
return res.VehicleStatus.Odometer.Value, nil
}
return 0, err
return res.VehicleStatus.Odometer.Value, err
}
var _ api.VehiclePosition = (*Provider)(nil)
@ -133,11 +129,7 @@ var _ api.VehiclePosition = (*Provider)(nil)
// Position implements the api.VehiclePosition interface
func (v *Provider) Position() (float64, float64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
return res.VehicleStatus.Gps.Latitude, res.VehicleStatus.Gps.Longitude, nil
}
return 0, 0, err
return res.VehicleStatus.Gps.Latitude, res.VehicleStatus.Gps.Longitude, err
}
var _ api.AlarmClock = (*Provider)(nil)

View file

@ -3,44 +3,46 @@ package vehicle
import (
"fmt"
"strings"
"github.com/thoas/go-funk"
)
// findVehicle finds the first vehicle in the list of VINs or returns an error
func findVehicle(vehicles []string, err error) (string, error) {
if err != nil {
return "", fmt.Errorf("cannot get vehicles: %w", err)
}
if len(vehicles) != 1 {
return "", fmt.Errorf("cannot find vehicle: %v", vehicles)
}
vin := strings.TrimSpace(vehicles[0])
if vin == "" {
return "", fmt.Errorf("cannot find vehicle: %v", vehicles)
}
return vin, nil
}
// ensureVehicle ensures that the vehicle is available on the api and returns the VIN
func ensureVehicle(vin string, fun func() ([]string, error)) (string, error) {
vehicles, err := fun()
if err != nil {
return "", fmt.Errorf("cannot get vehicles: %w", err)
}
if vin = strings.ToUpper(vin); vin != "" {
// vin defined but doesn't exist
if !funk.ContainsString(vehicles, vin) {
err = fmt.Errorf("cannot find vehicle: %s", vin)
}
} else {
// vin empty
vin, err = findVehicle(vehicles, nil)
}
// ensureVehicleWithFeature extracts VIN from list of VINs returned from `list`` function
func ensureVehicle(vin string, list func() ([]string, error)) (string, error) {
vin, _, err := ensureVehicleWithFeature(vin, list, func(v string) (string, string) {
return v, ""
})
return vin, err
}
// ensureVehicleWithFeature extracts VIN and feature from list of vehicles of type V returned from `list`` function
func ensureVehicleWithFeature[Vehicle, Feature any](
vin string,
list func() ([]Vehicle, error),
extract func(Vehicle) (string, Feature),
) (string, Feature, error) {
vehicles, err := list()
if err != nil {
return "", *new(Feature), fmt.Errorf("cannot get vehicles: %w", err)
}
if vin = strings.ToUpper(vin); vin != "" {
for _, vehicle := range vehicles {
if v, res := extract(vehicle); v == vin {
return v, res, nil
}
}
// vin defined but doesn't exist
err = fmt.Errorf("cannot find vehicle: %s", vin)
} else {
// vin empty
if len(vehicles) == 1 {
vin, res := extract(vehicles[0])
return vin, res, nil
}
err = fmt.Errorf("cannot find vehicle: %v", vehicles)
}
return "", *new(Feature), err
}

View file

@ -8,19 +8,19 @@ import (
)
type Provider struct {
statusG func() (interface{}, error)
positionG func() (interface{}, error)
statusG func() (StatusResponse, error)
positionG func() (PositionResponse, error)
actionS func(bool) error
}
func NewProvider(api *API, vin, user string, cache time.Duration) *Provider {
impl := &Provider{
statusG: provider.NewCached(func() (interface{}, error) {
statusG: provider.Cached(func() (StatusResponse, error) {
return api.Status(vin)
}, cache).InterfaceGetter(),
positionG: provider.NewCached(func() (interface{}, error) {
}, cache),
positionG: provider.Cached(func() (PositionResponse, error) {
return api.Position(vin)
}, cache).InterfaceGetter(),
}, cache),
actionS: func(start bool) error {
return api.ChargeAction(vin, user, start)
},
@ -35,7 +35,7 @@ var _ api.Battery = (*Provider)(nil)
func (v *Provider) SoC() (float64, error) {
var val float64
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
val, err = res.VehicleStatus.EvStatus.FloatVal("EV_STATE_OF_CHARGE")
}
@ -48,7 +48,7 @@ var _ api.VehicleRange = (*Provider)(nil)
func (v *Provider) Range() (int64, error) {
var val int64
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
val, err = res.VehicleStatus.EvStatus.IntVal("EV_RANGE_ON_BATTERY_KM")
}
@ -62,7 +62,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
if s, err := res.VehicleStatus.EvStatus.StringVal("EV_CHARGING_STATUS"); err == nil {
switch s {
case "NOTCONNECTED":
@ -83,7 +83,7 @@ var _ api.VehicleFinishTimer = (*Provider)(nil)
// FinishTime implements the api.VehicleFinishTimer interface
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
i, err := res.VehicleStatus.CoreStatus.IntVal("EV_MINUTES_TO_FULLY_CHARGED")
return time.Now().Add(time.Duration(i) * time.Minute), err
}
@ -97,7 +97,7 @@ var _ api.VehicleOdometer = (*Provider)(nil)
func (v *Provider) Odometer() (float64, error) {
var val float64
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
val, err = res.VehicleStatus.CoreStatus.FloatVal("ODOMETER")
}
@ -109,7 +109,7 @@ var _ api.VehiclePosition = (*Provider)(nil)
// Position implements the api.VehiclePosition interface
func (v *Provider) Position() (float64, float64, error) {
res, err := v.positionG()
if res, ok := res.(PositionResponse); err == nil && ok {
if err == nil {
return res.Position.Latitude, res.Position.Longitude, nil
}

View file

@ -14,7 +14,6 @@ import (
type Mercedes struct {
*embed
*mercedes.Provider
*mercedes.Identity
}
func init() {
@ -65,7 +64,6 @@ func NewMercedesFromConfig(other map[string]interface{}) (api.Vehicle, error) {
v := &Mercedes{
embed: &cc.embed,
Identity: identity,
Provider: mercedes.NewProvider(api, strings.ToUpper(cc.VIN), cc.Cache),
}

View file

@ -8,19 +8,19 @@ import (
// Provider implements the evcc vehicle api
type Provider struct {
chargerG func() (interface{}, error)
rangeG func() (interface{}, error)
chargerG func() (EVResponse, error)
rangeG func() (EVResponse, error)
}
// NewProvider provides the evcc vehicle api provider
func NewProvider(api *API, vin string, cache time.Duration) *Provider {
impl := &Provider{
chargerG: provider.NewCached(func() (interface{}, error) {
chargerG: provider.Cached(func() (EVResponse, error) {
return api.SoC(vin)
}, cache).InterfaceGetter(),
rangeG: provider.NewCached(func() (interface{}, error) {
}, cache),
rangeG: provider.Cached(func() (EVResponse, error) {
return api.Range(vin)
}, cache).InterfaceGetter(),
}, cache),
}
return impl
}
@ -28,7 +28,7 @@ func NewProvider(api *API, vin string, cache time.Duration) *Provider {
// SoC implements the api.Vehicle interface
func (v *Provider) SoC() (float64, error) {
res, err := v.chargerG()
if res, ok := res.(EVResponse); err == nil && ok {
if err == nil {
return float64(res.SoC.Value), nil
}
@ -38,7 +38,7 @@ func (v *Provider) SoC() (float64, error) {
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (rng int64, err error) {
res, err := v.chargerG()
if res, ok := res.(EVResponse); err == nil && ok {
if err == nil {
return int64(res.RangeElectric.Value), nil
}

View file

@ -12,7 +12,7 @@ const refreshTimeout = 2 * time.Minute
// Provider is a kamereon provider
type Provider struct {
statusG func() (interface{}, error)
statusG func() (StatusResponse, error)
action func(value Action) error
expiry time.Duration
refreshTime time.Time
@ -28,12 +28,12 @@ func NewProvider(api *API, vin string, expiry, cache time.Duration) *Provider {
expiry: expiry,
}
impl.statusG = provider.NewCached(func() (interface{}, error) {
impl.statusG = provider.Cached(func() (StatusResponse, error) {
return impl.status(
func() (StatusResponse, error) { return api.BatteryStatus(vin) },
func() (ActionResponse, error) { return api.RefreshRequest(vin, "RefreshBatteryStatus") },
)
}, cache).InterfaceGetter()
}, cache)
return impl
}
@ -85,7 +85,7 @@ var _ api.Battery = (*Provider)(nil)
func (v *Provider) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
return float64(res.Attributes.BatteryLevel), nil
}
@ -99,7 +99,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
if res.Attributes.PlugStatus > 0 {
status = api.StatusB
}
@ -117,7 +117,7 @@ var _ api.VehicleRange = (*Provider)(nil)
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
return int64(res.Attributes.RangeHvacOff), nil
}
@ -130,7 +130,7 @@ var _ api.VehicleFinishTimer = (*Provider)(nil)
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
if res.Attributes.RemainingTime == nil {
return time.Time{}, api.ErrNotAvailable
}

View file

@ -23,7 +23,7 @@ type Niu struct {
user, password string
serial string
token niu.Token
apiG func() (interface{}, error)
apiG func() (niu.Response, error)
}
func init() {
@ -58,7 +58,7 @@ func NewNiuFromConfig(other map[string]interface{}) (api.Vehicle, error) {
serial: strings.ToUpper(cc.Serial),
}
v.apiG = provider.NewCached(v.batteryAPI, cc.Cache).InterfaceGetter()
v.apiG = provider.Cached(v.batteryAPI, cc.Cache)
return v, nil
}
@ -116,7 +116,7 @@ func (v *Niu) request(uri string) (*http.Request, error) {
}
// batteryAPI provides battery api response
func (v *Niu) batteryAPI() (interface{}, error) {
func (v *Niu) batteryAPI() (niu.Response, error) {
var res niu.Response
req, err := v.request(niu.ApiURI + "/v3/motor_data/index_info?sn=" + v.serial)
@ -130,12 +130,7 @@ func (v *Niu) batteryAPI() (interface{}, error) {
// SoC implements the api.Vehicle interface
func (v *Niu) SoC() (float64, error) {
res, err := v.apiG()
if res, ok := res.(niu.Response); err == nil && ok {
return float64(res.Data.Batteries.CompartmentA.BatteryCharging), nil
}
return 0, err
return float64(res.Data.Batteries.CompartmentA.BatteryCharging), err
}
var _ api.ChargeState = (*Niu)(nil)
@ -145,7 +140,7 @@ func (v *Niu) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.apiG()
if res, ok := res.(niu.Response); err == nil && ok {
if err == nil {
if res.Data.IsConnected {
status = api.StatusB
}
@ -162,10 +157,5 @@ var _ api.VehicleRange = (*Niu)(nil)
// Range implements the api.VehicleRange interface
func (v *Niu) Range() (int64, error) {
res, err := v.apiG()
if res, ok := res.(niu.Response); err == nil && ok {
return res.Data.EstimatedMileage, nil
}
return 0, err
return res.Data.EstimatedMileage, err
}

View file

@ -40,12 +40,13 @@ type ovmsConnectResponse struct {
type Ovms struct {
*embed
*request.Helper
user, password, vehicleId, server string
cache time.Duration
isOnline bool
chargeG func() (interface{}, error)
statusG func() (interface{}, error)
locationG func() (interface{}, error)
user, password string
vehicleId, server string
cache time.Duration
isOnline bool
chargeG func() (ovmsChargeResponse, error)
statusG func() (ovmsStatusResponse, error)
locationG func() (ovmsLocationResponse, error)
}
func init() {
@ -78,9 +79,9 @@ func NewOvmsFromConfig(other map[string]interface{}) (api.Vehicle, error) {
cache: cc.Cache,
}
v.chargeG = provider.NewCached(v.batteryAPI, cc.Cache).InterfaceGetter()
v.statusG = provider.NewCached(v.statusAPI, cc.Cache).InterfaceGetter()
v.locationG = provider.NewCached(v.locationAPI, cc.Cache).InterfaceGetter()
v.chargeG = provider.Cached(v.batteryAPI, cc.Cache)
v.statusG = provider.Cached(v.statusAPI, cc.Cache)
v.locationG = provider.Cached(v.locationAPI, cc.Cache)
var err error
v.Jar, err = cookiejar.New(&cookiejar.Options{
@ -142,7 +143,7 @@ func (v *Ovms) authFlow() error {
}
// batteryAPI provides battery-status api response
func (v *Ovms) batteryAPI() (interface{}, error) {
func (v *Ovms) batteryAPI() (ovmsChargeResponse, error) {
var resp ovmsChargeResponse
resp, err := v.chargeRequest()
@ -162,7 +163,7 @@ func (v *Ovms) batteryAPI() (interface{}, error) {
}
// statusAPI provides vehicle status api response
func (v *Ovms) statusAPI() (interface{}, error) {
func (v *Ovms) statusAPI() (ovmsStatusResponse, error) {
var resp ovmsStatusResponse
resp, err := v.statusRequest()
@ -177,7 +178,7 @@ func (v *Ovms) statusAPI() (interface{}, error) {
}
// location API provides vehicle position api response
func (v *Ovms) locationAPI() (interface{}, error) {
func (v *Ovms) locationAPI() (ovmsLocationResponse, error) {
var resp ovmsLocationResponse
resp, err := v.locationRequest()
@ -194,12 +195,7 @@ func (v *Ovms) locationAPI() (interface{}, error) {
// SoC implements the api.Vehicle interface
func (v *Ovms) SoC() (float64, error) {
res, err := v.chargeG()
if res, ok := res.(ovmsChargeResponse); err == nil && ok {
return res.Soc, nil
}
return 0, err
return res.Soc, err
}
var _ api.ChargeState = (*Ovms)(nil)
@ -209,7 +205,7 @@ func (v *Ovms) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.chargeG()
if res, ok := res.(ovmsChargeResponse); err == nil && ok {
if err == nil {
if res.ChargePortOpen > 0 {
status = api.StatusB
}
@ -227,7 +223,7 @@ var _ api.VehicleRange = (*Ovms)(nil)
func (v *Ovms) Range() (int64, error) {
res, err := v.chargeG()
if res, ok := res.(ovmsChargeResponse); err == nil && ok {
if err == nil {
return strconv.ParseInt(res.EstimatedRange, 0, 64)
}
@ -239,12 +235,7 @@ var _ api.VehicleOdometer = (*Ovms)(nil)
// Odometer implements the api.VehicleOdometer interface
func (v *Ovms) Odometer() (float64, error) {
res, err := v.statusG()
if res, ok := res.(ovmsStatusResponse); err == nil && ok {
return res.Odometer / 10, nil
}
return 0, err
return res.Odometer / 10, err
}
var _ api.VehicleFinishTimer = (*Ovms)(nil)
@ -252,21 +243,11 @@ var _ api.VehicleFinishTimer = (*Ovms)(nil)
// FinishTime implements the api.VehicleFinishTimer interface
func (v *Ovms) FinishTime() (time.Time, error) {
res, err := v.chargeG()
if res, ok := res.(ovmsChargeResponse); err == nil && ok {
return time.Now().Add(time.Duration(res.ChargeEtrFull) * time.Minute), nil
}
return time.Time{}, err
return time.Now().Add(time.Duration(res.ChargeEtrFull) * time.Minute), err
}
// VehiclePosition returns the vehicles position in latitude and longitude
func (v *Ovms) Position() (float64, float64, error) {
res, err := v.locationG()
if res, ok := res.(ovmsLocationResponse); err == nil && ok {
return res.Latitude, res.Longitude, nil
}
return 0, 0, err
return res.Latitude, res.Longitude, err
}

View file

@ -8,7 +8,7 @@ import (
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/vehicle/porsche"
"github.com/thoas/go-funk"
"github.com/samber/lo"
)
// Porsche is an api.Vehicle implementation for Porsche cars
@ -53,15 +53,15 @@ func NewPorscheFromConfig(other map[string]interface{}) (api.Vehicle, error) {
cc.VIN, err = ensureVehicle(cc.VIN, func() ([]string, error) {
mobileVehicles, err := mobile.Vehicles()
if err == nil {
return funk.Map(mobileVehicles, func(v porsche.StatusResponseMobile) string {
return lo.Map(mobileVehicles, func(v porsche.StatusResponseMobile, _ int) string {
return v.VIN
}).([]string), err
}), err
}
vehicles, err := api.Vehicles()
return funk.Map(vehicles, func(v porsche.Vehicle) string {
return lo.Map(vehicles, func(v porsche.Vehicle, _ int) string {
return v.VIN
}).([]string), err
}), err
})
if err != nil {

View file

@ -11,28 +11,28 @@ import (
// Provider is an api.Vehicle implementation for Porsche PHEV cars
type Provider struct {
statusG func() (interface{}, error)
emobilityG func() (interface{}, error)
mobileG func() (interface{}, error)
statusG func() (StatusResponse, error)
emobilityG func() (EmobilityResponse, error)
mobileG func() (StatusResponseMobile, error)
}
// NewProvider creates a new vehicle
func NewProvider(log *util.Logger, api *API, emobility *EmobilityAPI, mobile *MobileAPI, vin string, carModel string, cache time.Duration) *Provider {
impl := &Provider{
statusG: provider.NewCached(func() (interface{}, error) {
statusG: provider.Cached(func() (StatusResponse, error) {
return api.Status(vin)
}, cache).InterfaceGetter(),
}, cache),
emobilityG: provider.NewCached(func() (interface{}, error) {
emobilityG: provider.Cached(func() (EmobilityResponse, error) {
if carModel != "" {
return emobility.Status(vin, carModel)
}
return EmobilityResponse{}, errors.New("no car model")
}, cache).InterfaceGetter(),
}, cache),
mobileG: provider.NewCached(func() (interface{}, error) {
mobileG: provider.Cached(func() (StatusResponseMobile, error) {
return mobile.Status(vin, []string{BATTERY_LEVEL, BATTERY_CHARGING_STATE, CLIMATIZER_STATE, E_RANGE, HEATING_STATE, MILEAGE})
}, cache).InterfaceGetter(),
}, cache),
}
return impl
@ -43,7 +43,7 @@ var _ api.Battery = (*Provider)(nil)
// SoC implements the api.Vehicle interface
func (v *Provider) SoC() (float64, error) {
res, err := v.mobileG()
if res, ok := res.(StatusResponseMobile); err == nil && ok {
if err == nil {
m, err := res.MeasurementByKey("BATTERY_LEVEL")
if err != nil && err != api.ErrNotAvailable {
return 0, err
@ -53,14 +53,14 @@ func (v *Provider) SoC() (float64, error) {
}
}
res, err = v.emobilityG()
if res, ok := res.(EmobilityResponse); err == nil && ok {
return float64(res.BatteryChargeStatus.StateOfChargeInPercentage), nil
res3, err := v.emobilityG()
if err == nil {
return float64(res3.BatteryChargeStatus.StateOfChargeInPercentage), nil
}
res, err = v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
return res.BatteryLevel.Value, nil
res2, err := v.statusG()
if err == nil {
return res2.BatteryLevel.Value, nil
}
return 0, err
@ -71,7 +71,7 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.mobileG()
if res, ok := res.(StatusResponseMobile); err == nil && ok {
if err == nil {
m, err := res.MeasurementByKey("E_RANGE")
if err != nil && err != api.ErrNotAvailable {
return 0, err
@ -81,14 +81,14 @@ func (v *Provider) Range() (int64, error) {
}
}
res, err = v.emobilityG()
if res, ok := res.(EmobilityResponse); err == nil && ok {
return res.BatteryChargeStatus.RemainingERange.ValueInKilometers, nil
res3, err := v.emobilityG()
if err == nil {
return res3.BatteryChargeStatus.RemainingERange.ValueInKilometers, nil
}
res, err = v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
return int64(res.RemainingRanges.ElectricalRange.Distance.Value), nil
res2, err := v.statusG()
if err == nil {
return int64(res2.RemainingRanges.ElectricalRange.Distance.Value), nil
}
return 0, err
@ -99,11 +99,12 @@ var _ api.VehicleFinishTimer = (*Provider)(nil)
// FinishTime implements the api.VehicleFinishTimer interface
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.mobileG()
if res, ok := res.(StatusResponseMobile); err == nil && ok {
if err == nil {
m, err := res.MeasurementByKey("BATTERY_CHARGING_STATE")
if err != nil && err != api.ErrNotAvailable {
return time.Time{}, err
}
if err != api.ErrNotAvailable {
if m.Value.EndsAt == "" {
if m.Value.LastModified != "" {
@ -115,9 +116,9 @@ func (v *Provider) FinishTime() (time.Time, error) {
}
}
res, err = v.emobilityG()
if res, ok := res.(EmobilityResponse); err == nil && ok {
return time.Now().Add(time.Duration(res.BatteryChargeStatus.RemainingChargeTimeUntil100PercentInMinutes) * time.Minute), err
res2, err := v.emobilityG()
if err == nil {
return time.Now().Add(time.Duration(res2.BatteryChargeStatus.RemainingChargeTimeUntil100PercentInMinutes) * time.Minute), err
}
return time.Time{}, err
@ -128,11 +129,12 @@ var _ api.ChargeState = (*Provider)(nil)
// Status implements the api.ChargeState interface
func (v *Provider) Status() (api.ChargeStatus, error) {
res, err := v.mobileG()
if res, ok := res.(StatusResponseMobile); err == nil && ok {
if err == nil {
m, err := res.MeasurementByKey("BATTERY_CHARGING_STATE")
if err != nil && err != api.ErrNotAvailable {
return api.StatusNone, err
}
if err != api.ErrNotAvailable {
switch m.Value.Status {
case "FAST_CHARGING", "NOT_PLUGGED", "UNKNOWN":
@ -150,17 +152,17 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
}
}
res, err = v.emobilityG()
if res, ok := res.(EmobilityResponse); err == nil && ok {
switch res.BatteryChargeStatus.PlugState {
res2, err := v.emobilityG()
if err == nil {
switch res2.BatteryChargeStatus.PlugState {
case "DISCONNECTED":
return api.StatusA, nil
case "CONNECTED":
// ignore if the car is connected to a DC charging station
if res.BatteryChargeStatus.ChargingInDCMode {
if res2.BatteryChargeStatus.ChargingInDCMode {
return api.StatusA, nil
}
switch res.BatteryChargeStatus.ChargingState {
switch res2.BatteryChargeStatus.ChargingState {
case "ERROR":
return api.StatusF, nil
case "OFF", "COMPLETED":
@ -168,7 +170,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
case "ON", "CHARGING":
return api.StatusC, nil
default:
return api.StatusNone, errors.New("emobility - unknown charging state: " + res.BatteryChargeStatus.ChargingState)
return api.StatusNone, errors.New("emobility - unknown charging state: " + res2.BatteryChargeStatus.ChargingState)
}
}
}
@ -181,7 +183,7 @@ var _ api.VehicleClimater = (*Provider)(nil)
// Climater implements the api.VehicleClimater interface
func (v *Provider) Climater() (active bool, outsideTemp float64, targetTemp float64, err error) {
res, err := v.mobileG()
if res, ok := res.(StatusResponseMobile); err == nil && ok {
if err == nil {
m, err := res.MeasurementByKey("CLIMATIZER_STATE")
if err != nil && err != api.ErrNotAvailable {
return active, 20, 20, err
@ -191,15 +193,15 @@ func (v *Provider) Climater() (active bool, outsideTemp float64, targetTemp floa
}
}
res, err = v.emobilityG()
if res, ok := res.(EmobilityResponse); err == nil && ok {
switch res.DirectClimatisation.ClimatisationState {
res2, err := v.emobilityG()
if err == nil {
switch res2.DirectClimatisation.ClimatisationState {
case "OFF":
return false, 20, 20, nil
case "ON":
return true, 20, 20, nil
default:
return active, outsideTemp, targetTemp, errors.New("emobility - unknown climate state: " + res.DirectClimatisation.ClimatisationState)
return active, outsideTemp, targetTemp, errors.New("emobility - unknown climate state: " + res2.DirectClimatisation.ClimatisationState)
}
}
@ -211,7 +213,7 @@ var _ api.VehicleOdometer = (*Provider)(nil)
// Odometer implements the api.VehicleOdometer interface
func (v *Provider) Odometer() (float64, error) {
res, err := v.mobileG()
if res, ok := res.(StatusResponseMobile); err == nil && ok {
if err == nil {
m, err := res.MeasurementByKey("MILEAGE")
if err != nil && err != api.ErrNotAvailable {
return 0, err
@ -221,9 +223,9 @@ func (v *Provider) Odometer() (float64, error) {
}
}
res, err = v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
return res.Mileage.Value, nil
res2, err := v.statusG()
if err == nil {
return res2.Mileage.Value, nil
}
return 0, err

View file

@ -1,9 +1,7 @@
package vehicle
import (
"errors"
"fmt"
"strings"
"time"
"github.com/evcc-io/evcc/api"
@ -98,26 +96,17 @@ func newPSA(log *util.Logger, brand, realm, id, secret string, other map[string]
api := psa.NewAPI(log, identity, realm, cc.Credentials.ID)
vehicles, err := api.Vehicles()
_, vid, err := ensureVehicleWithFeature(
cc.VIN, api.Vehicles,
func(v psa.Vehicle) (string, string) {
return v.VIN, v.ID
},
)
if err != nil {
return nil, err
}
var vid string
if cc.VIN == "" && len(vehicles) == 1 {
vid = vehicles[0].ID
} else {
for _, vehicle := range vehicles {
if vehicle.VIN == strings.ToUpper(cc.VIN) {
vid = vehicle.ID
}
}
}
if vid == "" {
return nil, errors.New("vin not found")
}
v.Provider = psa.NewProvider(api, vid, cc.Cache)
return v, err

View file

@ -10,15 +10,15 @@ import (
// Provider is an api.Vehicle implementation for PSA cars
type Provider struct {
statusG func() (interface{}, error)
statusG func() (Status, error)
}
// NewProvider creates a new vehicle
func NewProvider(api *API, vid string, cache time.Duration) *Provider {
impl := &Provider{
statusG: provider.NewCached(func() (interface{}, error) {
statusG: provider.Cached(func() (Status, error) {
return api.Status(vid)
}, cache).InterfaceGetter(),
}, cache),
}
return impl
}
@ -28,7 +28,7 @@ var _ api.Battery = (*Provider)(nil)
// SoC implements the api.Vehicle interface
func (v *Provider) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
for _, e := range res.Energy {
if e.Type != "Electric" {
continue
@ -48,7 +48,7 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
for _, e := range res.Energy {
if e.Type != "Electric" {
continue
@ -69,7 +69,7 @@ var _ api.VehicleOdometer = (*Provider)(nil)
func (v *Provider) Odometer() (float64, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
return res.Odometer.Mileage, nil
}
@ -81,7 +81,7 @@ var _ api.VehicleFinishTimer = (*Provider)(nil)
// FinishTime implements the api.VehicleFinishTimer interface
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
for _, e := range res.Energy {
if e.Type != "Electric" {
continue
@ -101,7 +101,7 @@ var _ api.ChargeState = (*Provider)(nil)
// Status implements the api.ChargeState interface
func (v *Provider) Status() (api.ChargeStatus, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
for _, e := range res.Energy {
if e.Type != "Electric" {
continue
@ -131,7 +131,7 @@ var _ api.VehicleClimater = (*Provider)(nil)
// Climater implements the api.VehicleClimater interface
func (v *Provider) Climater() (active bool, outsideTemp float64, targetTemp float64, err error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
active := strings.ToLower(res.Preconditionning.AirConditioning.Status) != "disabled"
return active, 20, 20, nil
}
@ -144,7 +144,7 @@ var _ api.VehiclePosition = (*Provider)(nil)
// Position implements the api.VehiclePosition interface
func (v *Provider) Position() (float64, float64, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
if coord := res.LastPosition.Geometry.Coordinates; len(coord) == 2 {
return coord[0], coord[1], nil
}

View file

@ -12,7 +12,7 @@ import (
"github.com/evcc-io/evcc/provider"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/thoas/go-funk"
"golang.org/x/exp/slices"
)
// Credits to
@ -105,9 +105,9 @@ type Renault struct {
gigya, kamereon configServer
gigyaJwtToken string
accountID string
batteryG func() (interface{}, error)
cockpitG func() (interface{}, error)
hvacG func() (interface{}, error)
batteryG func() (kamereonResponse, error)
cockpitG func() (kamereonResponse, error)
hvacG func() (kamereonResponse, error)
}
func init() {
@ -150,11 +150,9 @@ func NewRenaultFromConfig(other map[string]interface{}) (api.Vehicle, error) {
})
}
if err == nil {
v.batteryG = provider.NewCached(v.batteryAPI, cc.Cache).InterfaceGetter()
v.cockpitG = provider.NewCached(v.cockpitAPI, cc.Cache).InterfaceGetter()
v.hvacG = provider.NewCached(v.hvacAPI, cc.Cache).InterfaceGetter()
}
v.batteryG = provider.Cached(v.batteryAPI, cc.Cache)
v.cockpitG = provider.Cached(v.cockpitAPI, cc.Cache)
v.hvacG = provider.Cached(v.hvacAPI, cc.Cache)
return v, err
}
@ -314,7 +312,7 @@ func (v *Renault) kamereonVehicles(accountID string) ([]string, error) {
}
// batteryAPI provides battery-status api response
func (v *Renault) batteryAPI() (interface{}, error) {
func (v *Renault) batteryAPI() (kamereonResponse, error) {
uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/kamereon/kca/car-adapter/v2/cars/%s/battery-status", v.kamereon.Target, v.accountID, v.vin)
res, err := v.kamereonRequest(uri)
@ -329,7 +327,7 @@ func (v *Renault) batteryAPI() (interface{}, error) {
}
// hvacAPI provides hvac-status api response
func (v *Renault) hvacAPI() (interface{}, error) {
func (v *Renault) hvacAPI() (kamereonResponse, error) {
uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/kamereon/kca/car-adapter/v1/cars/%s/hvac-status", v.kamereon.Target, v.accountID, v.vin)
res, err := v.kamereonRequest(uri)
@ -344,7 +342,7 @@ func (v *Renault) hvacAPI() (interface{}, error) {
}
// cockpitAPI provides cockpit api response
func (v *Renault) cockpitAPI() (interface{}, error) {
func (v *Renault) cockpitAPI() (kamereonResponse, error) {
uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/kamereon/kca/car-adapter/v2/cars/%s/cockpit", v.kamereon.Target, v.accountID, v.vin)
res, err := v.kamereonRequest(uri)
@ -362,7 +360,7 @@ func (v *Renault) cockpitAPI() (interface{}, error) {
func (v *Renault) SoC() (float64, error) {
res, err := v.batteryG()
if res, ok := res.(kamereonResponse); err == nil && ok {
if err == nil {
return float64(res.Data.Attributes.BatteryLevel), nil
}
@ -376,7 +374,7 @@ func (v *Renault) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.batteryG()
if res, ok := res.(kamereonResponse); err == nil && ok {
if err == nil {
if res.Data.Attributes.PlugStatus > 0 {
status = api.StatusB
}
@ -394,7 +392,7 @@ var _ api.VehicleRange = (*Renault)(nil)
func (v *Renault) Range() (int64, error) {
res, err := v.batteryG()
if res, ok := res.(kamereonResponse); err == nil && ok {
if err == nil {
return int64(res.Data.Attributes.BatteryAutonomy), nil
}
@ -407,7 +405,7 @@ var _ api.VehicleOdometer = (*Renault)(nil)
func (v *Renault) Odometer() (float64, error) {
res, err := v.cockpitG()
if res, ok := res.(kamereonResponse); err == nil && ok {
if err == nil {
return res.Data.Attributes.TotalMileage, nil
}
@ -420,7 +418,7 @@ var _ api.VehicleFinishTimer = (*Renault)(nil)
func (v *Renault) FinishTime() (time.Time, error) {
res, err := v.batteryG()
if res, ok := res.(kamereonResponse); err == nil && ok {
if err == nil {
timestamp, err := time.Parse(time.RFC3339, res.Data.Attributes.Timestamp)
if res.Data.Attributes.RemainingTime == nil {
@ -444,14 +442,14 @@ func (v *Renault) Climater() (active bool, outsideTemp float64, targetTemp float
return false, 0, 0, api.ErrNotAvailable
}
if res, ok := res.(kamereonResponse); err == nil && ok {
if err == nil {
state := strings.ToLower(res.Data.Attributes.HvacStatus)
if state == "" {
return false, 0, 0, api.ErrNotAvailable
}
active := !funk.ContainsString([]string{"off", "false", "invalid", "error"}, state)
active := !slices.Contains([]string{"off", "false", "invalid", "error"}, state)
return active, res.Data.Attributes.ExternalTemperature, 20, nil
}

View file

@ -10,16 +10,16 @@ import (
// Provider is an api.Vehicle implementation for Seat Cupra cars
type Provider struct {
statusG func() (interface{}, error)
statusG func() (Status, error)
action func(string, string) error
}
// NewProvider creates a new vehicle
func NewProvider(api *API, userID, vin string, cache time.Duration) *Provider {
impl := &Provider{
statusG: provider.NewCached(func() (interface{}, error) {
statusG: provider.Cached(func() (Status, error) {
return api.Status(userID, vin)
}, cache).InterfaceGetter(),
}, cache),
action: func(action, cmd string) error {
return api.Action(vin, action, cmd)
},
@ -32,11 +32,7 @@ var _ api.Battery = (*Provider)(nil)
// SoC implements the api.Vehicle interface
func (v *Provider) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
return float64(res.Engines.Primary.Level), nil
}
return 0, err
return float64(res.Engines.Primary.Level), err
}
var _ api.ChargeState = (*Provider)(nil)
@ -46,7 +42,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
switch strings.ToLower(res.Services.Charging.Status) {
case "connected":
status = api.StatusB
@ -63,7 +59,7 @@ var _ api.VehicleFinishTimer = (*Provider)(nil)
// FinishTime implements the api.VehicleFinishTimer interface
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
rsc := res.Services.Charging
if !rsc.Active {
return time.Time{}, api.ErrNotAvailable
@ -85,11 +81,7 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
return int64(res.Engines.Primary.Range.Value), nil
}
return 0, err
return int64(res.Engines.Primary.Range.Value), err
}
var _ api.VehicleClimater = (*Provider)(nil)
@ -97,11 +89,7 @@ var _ api.VehicleClimater = (*Provider)(nil)
// Climater implements the api.VehicleClimater interface
func (v *Provider) Climater() (active bool, outsideTemp float64, targetTemp float64, err error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
return res.Services.Climatisation.Active, 21, 21, nil
}
return active, 21, 21, err
return res.Services.Climatisation.Active, 21, 21, err
}
var _ api.VehicleChargeController = (*Provider)(nil)

View file

@ -14,7 +14,7 @@ import (
// Silence is an api.Vehicle implementation for Silence S01 vehicles
type Silence struct {
*embed
apiG func() (interface{}, error)
apiG func() (silence.Vehicle, error)
}
func init() {
@ -55,9 +55,9 @@ func NewSilenceFromConfig(other map[string]interface{}) (api.Vehicle, error) {
vin, err := ensureVehicle(strings.ToLower(cc.VIN), api.Vehicles)
if err == nil {
v.apiG = provider.NewCached(func() (interface{}, error) {
v.apiG = provider.Cached(func() (silence.Vehicle, error) {
return api.Status(vin)
}, cc.Cache).InterfaceGetter()
}, cc.Cache)
}
return v, err
@ -66,12 +66,7 @@ func NewSilenceFromConfig(other map[string]interface{}) (api.Vehicle, error) {
// SoC implements the api.Vehicle interface
func (v *Silence) SoC() (float64, error) {
res, err := v.apiG()
if res, ok := res.(silence.Vehicle); err == nil && ok {
return float64(res.BatterySoc), nil
}
return 0, err
return float64(res.BatterySoc), err
}
var _ api.VehicleRange = (*Silence)(nil)
@ -79,12 +74,7 @@ var _ api.VehicleRange = (*Silence)(nil)
// Range implements the api.VehicleRange interface
func (v *Silence) Range() (int64, error) {
res, err := v.apiG()
if res, ok := res.(silence.Vehicle); err == nil && ok {
return int64(res.Range), nil
}
return 0, err
return int64(res.Range), err
}
var _ api.VehicleOdometer = (*Silence)(nil)
@ -92,10 +82,5 @@ var _ api.VehicleOdometer = (*Silence)(nil)
// Odometer implements the api.VehicleOdometer interface
func (v *Silence) Odometer() (float64, error) {
res, err := v.apiG()
if res, ok := res.(silence.Vehicle); err == nil && ok {
return float64(res.Odometer), nil
}
return 0, err
return float64(res.Odometer), err
}

View file

@ -5,7 +5,7 @@ import (
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/thoas/go-funk"
"github.com/samber/lo"
"golang.org/x/oauth2"
)
@ -40,9 +40,9 @@ func (v *API) Vehicles() ([]string, error) {
err := v.GetJSON(ApiUri, &resp)
if err == nil {
return funk.Map(resp, func(v Vehicle) string {
return lo.Map(resp, func(v Vehicle, _ int) string {
return v.FrameNo
}).([]string), nil
}), nil
}
return nil, err

View file

@ -9,19 +9,19 @@ import (
// Provider implements the evcc vehicle api
type Provider struct {
chargerG func() (interface{}, error)
chargerG func() (ChargerResponse, error)
action func(action, value string) error
}
// NewProvider provides the evcc vehicle api provider
func NewProvider(api *API, vin string, cache time.Duration) *Provider {
impl := &Provider{
chargerG: provider.NewCached(func() (interface{}, error) {
chargerG: provider.Cached(func() (ChargerResponse, error) {
return api.Charger(vin)
}, cache).InterfaceGetter(),
// climateG: provider.NewCached(func() (interface{}, error) {
}, cache),
// climateG: provider.Cached(func() (interface{}, error) {
// return api.Climater(vin)
// }, cache).InterfaceGetter(),
// }, cache),
action: func(action, value string) error {
return api.Action(vin, action, value)
},
@ -34,7 +34,7 @@ var _ api.Battery = (*Provider)(nil)
// SoC implements the api.Vehicle interface
func (v *Provider) SoC() (float64, error) {
res, err := v.chargerG()
if res, ok := res.(ChargerResponse); err == nil && ok {
if err == nil {
return float64(res.Battery.StateOfChargeInPercent), nil
}
@ -48,7 +48,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.chargerG()
if res, ok := res.(ChargerResponse); err == nil && ok {
if err == nil {
if res.Plug.ConnectionState == "Connected" {
status = api.StatusB
}
@ -65,7 +65,7 @@ var _ api.VehicleFinishTimer = (*Provider)(nil)
// FinishTime implements the api.VehicleFinishTimer interface
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.chargerG()
if res, ok := res.(ChargerResponse); err == nil && ok {
if err == nil {
crg := res.Charging
// estimate not available
@ -85,7 +85,7 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (rng int64, err error) {
res, err := v.chargerG()
if res, ok := res.(ChargerResponse); err == nil && ok {
if err == nil {
rng = res.Battery.CruisingRangeElectricInMeters / 1e3
}
@ -97,7 +97,7 @@ func (v *Provider) Range() (rng int64, err error) {
// // Climater implements the api.VehicleClimater interface
// func (v *Provider) Climater() (active bool, outsideTemp float64, targetTemp float64, err error) {
// res, err := v.climateG()
// if res, ok := res.(ClimaterResponse); err == nil && ok {
// err == nil {
// state := strings.ToLower(res.Climater.Status.ClimatisationStatusData.ClimatisationState.Content)
// active := state != "off" && state != "invalid" && state != "error"

View file

@ -51,12 +51,7 @@ func NewSmartFromConfig(other map[string]interface{}) (api.Vehicle, error) {
api := smart.NewAPI(log, identity)
if cc.VIN == "" {
cc.VIN, err = findVehicle(api.Vehicles())
if err == nil {
log.DEBUG.Printf("found vehicle: %v", cc.VIN)
}
}
cc.VIN, err = ensureVehicle(cc.VIN, api.Vehicles)
if err == nil {
v.Provider = smart.NewProvider(log, api, cc.VIN, cc.Expiry, cc.Cache)

View file

@ -7,7 +7,7 @@ import (
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/transport"
"github.com/evcc-io/evcc/vehicle/mb"
"github.com/thoas/go-funk"
"github.com/samber/lo"
"golang.org/x/oauth2"
)
@ -69,9 +69,9 @@ func (v *API) Vehicles() ([]string, error) {
err = fmt.Errorf("%s (%s): %w", res.Error, res.ErrorDescription, err)
}
vehicles := funk.Map(res.LicensePlates, func(v vehicle) string {
vehicles := lo.Map(res.LicensePlates, func(v vehicle, _ int) string {
return v.FIN
}).([]string)
})
return vehicles, err
}

View file

@ -11,7 +11,7 @@ import (
// https://github.com/TA2k/ioBroker.smart-eq
type Provider struct {
statusG func() (interface{}, error)
statusG func() (StatusResponse, error)
expiry time.Duration
}
@ -20,12 +20,12 @@ func NewProvider(log *util.Logger, api *API, vin string, expiry, cache time.Dura
expiry: expiry,
}
v.statusG = provider.NewCached(func() (interface{}, error) {
v.statusG = provider.Cached(func() (StatusResponse, error) {
return v.status(
func() (StatusResponse, error) { return api.Status(vin) },
func() (StatusResponse, error) { return api.Refresh(vin) },
)
}, cache).InterfaceGetter()
}, cache)
return v
}
@ -43,12 +43,7 @@ func (v *Provider) status(statusG func() (StatusResponse, error), refreshG func(
// SoC implements the api.Vehicle interface
func (v *Provider) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
return res.Status.StatusData.Soc.Value, nil
}
return 0, err
return res.Status.StatusData.Soc.Value, err
}
var _ api.VehicleRange = (*Provider)(nil)
@ -56,12 +51,7 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
return int64(res.Status.StatusData.RangeElectric.Value), nil
}
return 0, err
return int64(res.Status.StatusData.RangeElectric.Value), err
}
var _ api.VehicleOdometer = (*Provider)(nil)
@ -69,10 +59,5 @@ var _ api.VehicleOdometer = (*Provider)(nil)
// Odometer implements the Provider.VehicleOdometer interface
func (v *Provider) Odometer() (float64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
return res.Status.StatusData.Odo.Value, nil
}
return 0, err
return res.Status.StatusData.Odo.Value, err
}

View file

@ -2,8 +2,6 @@ package vehicle
import (
"context"
"errors"
"strings"
"time"
"github.com/bogosj/tesla"
@ -18,9 +16,9 @@ import (
type Tesla struct {
*embed
vehicle *tesla.Vehicle
chargeStateG func() (interface{}, error)
vehicleStateG func() (interface{}, error)
driveStateG func() (interface{}, error)
chargeStateG func() (*tesla.ChargeState, error)
vehicleStateG func() (*tesla.VehicleState, error)
driveStateG func() (*tesla.DriveState, error)
}
func init() {
@ -65,56 +63,33 @@ func NewTeslaFromConfig(other map[string]interface{}) (api.Vehicle, error) {
return nil, err
}
vehicles, err := client.Vehicles()
cc.VIN, v.vehicle, err = ensureVehicleWithFeature(
cc.VIN, client.Vehicles,
func(v *tesla.Vehicle) (string, *tesla.Vehicle) {
return v.Vin, v
},
)
if err != nil {
return nil, err
}
if cc.VIN == "" && len(vehicles) == 1 {
v.vehicle = vehicles[0]
} else {
for _, vehicle := range vehicles {
if vehicle.Vin == strings.ToUpper(cc.VIN) {
v.vehicle = vehicle
}
}
}
if v.vehicle == nil {
return nil, errors.New("vin not found")
}
if v.Title_ == "" {
v.Title_ = v.vehicle.DisplayName
}
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).InterfaceGetter()
v.vehicleStateG = provider.NewCached(v.vehicleState, cc.Cache).InterfaceGetter()
v.driveStateG = provider.NewCached(v.driveState, cc.Cache).InterfaceGetter()
v.chargeStateG = provider.Cached(v.vehicle.ChargeState, cc.Cache)
v.vehicleStateG = provider.Cached(v.vehicle.VehicleState, cc.Cache)
v.driveStateG = provider.Cached(v.vehicle.DriveState, cc.Cache)
return v, nil
}
// chargeState implements the charge state api
func (v *Tesla) chargeState() (interface{}, error) {
return v.vehicle.ChargeState()
}
// vehicleState implements the climater api
func (v *Tesla) vehicleState() (interface{}, error) {
return v.vehicle.VehicleState()
}
// driveState implements the climater api
func (v *Tesla) driveState() (interface{}, error) {
return v.vehicle.DriveState()
}
// SoC implements the api.Vehicle interface
func (v *Tesla) SoC() (float64, error) {
res, err := v.chargeStateG()
if res, ok := res.(*tesla.ChargeState); err == nil && ok {
if err == nil {
return float64(res.UsableBatteryLevel), nil
}
@ -128,7 +103,7 @@ func (v *Tesla) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.chargeStateG()
if res, ok := res.(*tesla.ChargeState); err == nil && ok {
if err == nil {
if res.ChargingState == "Stopped" || res.ChargingState == "NoPower" || res.ChargingState == "Complete" {
status = api.StatusB
}
@ -146,7 +121,7 @@ var _ api.ChargeRater = (*Tesla)(nil)
func (v *Tesla) ChargedEnergy() (float64, error) {
res, err := v.chargeStateG()
if res, ok := res.(*tesla.ChargeState); err == nil && ok {
if err == nil {
return res.ChargeEnergyAdded, nil
}
@ -161,7 +136,7 @@ var _ api.VehicleRange = (*Tesla)(nil)
func (v *Tesla) Range() (int64, error) {
res, err := v.chargeStateG()
if res, ok := res.(*tesla.ChargeState); err == nil && ok {
if err == nil {
// miles to km
return int64(kmPerMile * res.BatteryRange), nil
}
@ -175,7 +150,7 @@ var _ api.VehicleOdometer = (*Tesla)(nil)
func (v *Tesla) Odometer() (float64, error) {
res, err := v.vehicleStateG()
if res, ok := res.(*tesla.VehicleState); err == nil && ok {
if err == nil {
// miles to km
return kmPerMile * res.Odometer, nil
}
@ -189,7 +164,7 @@ var _ api.VehicleFinishTimer = (*Tesla)(nil)
func (v *Tesla) FinishTime() (time.Time, error) {
res, err := v.chargeStateG()
if res, ok := res.(*tesla.ChargeState); err == nil && ok {
if err == nil {
t := time.Now()
return t.Add(time.Duration(res.MinutesToFullCharge) * time.Minute), err
}
@ -204,7 +179,7 @@ var _ api.VehiclePosition = (*Tesla)(nil)
// Position implements the api.VehiclePosition interface
func (v *Tesla) Position() (float64, float64, error) {
res, err := v.driveStateG()
if res, ok := res.(*tesla.DriveState); err == nil && ok {
if err == nil {
return res.Latitude, res.Longitude, nil
}

View file

@ -19,10 +19,8 @@ package vehicle
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"time"
"github.com/evcc-io/evcc/api"
@ -32,7 +30,7 @@ import (
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/sponsor"
"github.com/evcc-io/evcc/vehicle/tronity"
"github.com/thoas/go-funk"
"golang.org/x/exp/slices"
"golang.org/x/oauth2"
)
@ -43,7 +41,7 @@ type Tronity struct {
log *util.Logger
oc *oauth2.Config
vid string
bulkG func() (interface{}, error)
bulkG func() (tronity.Bulk, error)
}
func init() {
@ -113,41 +111,32 @@ func NewTronityFromConfig(other map[string]interface{}) (api.Vehicle, error) {
Base: v.Client.Transport,
}
vehicles, err := v.vehicles()
_, vehicle, err := ensureVehicleWithFeature(
cc.VIN, v.vehicles,
func(v tronity.Vehicle) (string, tronity.Vehicle) {
return v.VIN, v
},
)
if err != nil {
return nil, err
}
var vehicle tronity.Vehicle
if cc.VIN == "" && len(vehicles) == 1 {
vehicle = vehicles[0]
} else {
for _, v := range vehicles {
if v.VIN == strings.ToUpper(cc.VIN) {
vehicle = v
}
}
}
if vehicle.ID == "" {
return nil, errors.New("vin not found")
}
v.vid = vehicle.ID
v.bulkG = provider.NewCached(v.bulk, cc.Cache).InterfaceGetter()
v.bulkG = provider.Cached(v.bulk, cc.Cache)
var status func() (api.ChargeStatus, error)
if funk.ContainsString(vehicle.Scopes, tronity.ReadCharge) {
if slices.Contains(vehicle.Scopes, tronity.ReadCharge) {
status = v.status
}
var odometer func() (float64, error)
if funk.ContainsString(vehicle.Scopes, tronity.ReadOdometer) {
if slices.Contains(vehicle.Scopes, tronity.ReadOdometer) {
odometer = v.odometer
}
var start, stop func() error
if funk.ContainsString(vehicle.Scopes, tronity.WriteChargeStartStop) {
if slices.Contains(vehicle.Scopes, tronity.WriteChargeStartStop) {
start = v.startCharge
stop = v.stopCharge
}
@ -189,7 +178,7 @@ func (v *Tronity) vehicles() ([]tronity.Vehicle, error) {
}
// bulk implements the bulk api
func (v *Tronity) bulk() (interface{}, error) {
func (v *Tronity) bulk() (tronity.Bulk, error) {
uri := fmt.Sprintf("%s/v1/vehicles/%s/bulk", tronity.URI, v.vid)
var res tronity.Bulk
@ -201,12 +190,7 @@ func (v *Tronity) bulk() (interface{}, error) {
// SoC implements the api.Vehicle interface
func (v *Tronity) SoC() (float64, error) {
res, err := v.bulkG()
if res, ok := res.(tronity.Bulk); err == nil && ok {
return res.Level, nil
}
return 0, err
return res.Level, err
}
// status implements the api.ChargeState interface
@ -214,7 +198,7 @@ func (v *Tronity) status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.bulkG()
if res, ok := res.(tronity.Bulk); err == nil && ok {
if err == nil {
if res.Charging == "Charging" {
status = api.StatusC
}
@ -228,23 +212,13 @@ var _ api.VehicleRange = (*Tronity)(nil)
// Range implements the api.VehicleRange interface
func (v *Tronity) Range() (int64, error) {
res, err := v.bulkG()
if res, ok := res.(tronity.Bulk); err == nil && ok {
return int64(res.Range), nil
}
return 0, err
return int64(res.Range), err
}
// odometer implements the api.VehicleOdometer interface
func (v *Tronity) odometer() (float64, error) {
res, err := v.bulkG()
if res, ok := res.(tronity.Bulk); err == nil && ok {
return res.Odometer, nil
}
return 0, err
return res.Odometer, err
}
func (v *Tronity) post(uri string) error {

View file

@ -17,7 +17,7 @@ type Volvo struct {
*embed
*request.Helper
vin string
statusG func() (interface{}, error)
statusG func() (volvo.Status, error)
}
func init() {
@ -60,9 +60,7 @@ func NewVolvoFromConfig(other map[string]interface{}) (api.Vehicle, error) {
}),
}
v.statusG = provider.NewCached(func() (interface{}, error) {
return v.status()
}, cc.Cache).InterfaceGetter()
v.statusG = provider.Cached(v.status, cc.Cache)
var err error
v.vin, err = ensureVehicle(cc.VIN, v.vehicles)
@ -109,11 +107,7 @@ func (v *Volvo) status() (volvo.Status, error) {
// SoC implements the api.Vehicle interface
func (v *Volvo) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(volvo.Status); err == nil && ok {
return float64(res.HvBattery.HvBatteryLevel), nil
}
return 0, err
return float64(res.HvBattery.HvBatteryLevel), err
}
var _ api.ChargeState = (*Volvo)(nil)
@ -121,7 +115,7 @@ var _ api.ChargeState = (*Volvo)(nil)
// Status implements the api.ChargeState interface
func (v *Volvo) Status() (api.ChargeStatus, error) {
res, err := v.statusG()
if res, ok := res.(volvo.Status); err == nil && ok {
if err == nil {
switch res.HvBattery.HvBatteryChargeStatusDerived {
case "CableNotPluggedInCar":
return api.StatusA, nil
@ -140,11 +134,7 @@ var _ api.VehicleRange = (*Volvo)(nil)
// VehicleRange implements the api.VehicleRange interface
func (v *Volvo) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(volvo.Status); err == nil && ok {
return int64(res.HvBattery.DistanceToHVBatteryEmpty), nil
}
return 0, err
return int64(res.HvBattery.DistanceToHVBatteryEmpty), err
}
var _ api.VehicleOdometer = (*Volvo)(nil)
@ -152,11 +142,7 @@ var _ api.VehicleOdometer = (*Volvo)(nil)
// VehicleOdometer implements the api.VehicleOdometer interface
func (v *Volvo) Odometer() (float64, error) {
res, err := v.statusG()
if res, ok := res.(volvo.Status); err == nil && ok {
return res.Odometer / 1e3, nil
}
return 0, err
return res.Odometer / 1e3, err
}
var _ api.VehicleFinishTimer = (*Volvo)(nil)
@ -164,7 +150,7 @@ var _ api.VehicleFinishTimer = (*Volvo)(nil)
// FinishTime implements the VehicleFinishTimer interface
func (v *Volvo) FinishTime() (time.Time, error) {
res, err := v.statusG()
if res, ok := res.(volvo.Status); err == nil && ok {
if err == nil {
timestamp := res.HvBattery.TimeToHVBatteryFullyChargedTimestamp.Add(time.Duration(res.HvBattery.DistanceToHVBatteryEmpty) * time.Minute)
if timestamp.Before(time.Now()) {
return time.Time{}, api.ErrNotAvailable

View file

@ -10,16 +10,16 @@ import (
// Provider is an api.Vehicle implementation for VW ID cars
type Provider struct {
statusG func() (interface{}, error)
statusG func() (Status, error)
action func(action, value string) error
}
// NewProvider creates a new vehicle
func NewProvider(api *API, vin string, cache time.Duration) *Provider {
impl := &Provider{
statusG: provider.NewCached(func() (interface{}, error) {
statusG: provider.Cached(func() (Status, error) {
return api.Status(vin)
}, cache).InterfaceGetter(),
}, cache),
action: func(action, value string) error {
return api.Action(vin, action, value)
},
@ -32,7 +32,7 @@ var _ api.Battery = (*Provider)(nil)
// SoC implements the api.Vehicle interface
func (v *Provider) SoC() (float64, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
return float64(res.Data.BatteryStatus.CurrentSOCPercent), nil
}
@ -46,7 +46,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
if res.Data.PlugStatus.PlugConnectionState == "connected" {
status = api.StatusB
}
@ -63,7 +63,7 @@ var _ api.VehicleFinishTimer = (*Provider)(nil)
// FinishTime implements the api.VehicleFinishTimer interface
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
cst := res.Data.ChargingStatus
return cst.CarCapturedTimestamp.Add(time.Duration(cst.RemainingChargingTimeToCompleteMin) * time.Minute), err
}
@ -76,7 +76,7 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
return int64(res.Data.BatteryStatus.CruisingRangeElectricKm), nil
}
@ -88,7 +88,7 @@ var _ api.VehicleOdometer = (*Provider)(nil)
// Odometer implements the api.VehicleOdometer interface
func (v *Provider) Odometer() (float64, error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
if res.Data.MaintenanceStatus == nil {
return 0, api.ErrNotAvailable
}
@ -103,7 +103,7 @@ var _ api.VehicleClimater = (*Provider)(nil)
// Climater implements the api.VehicleClimater interface
func (v *Provider) Climater() (active bool, outsideTemp float64, targetTemp float64, err error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if err == nil {
state := strings.ToLower(res.Data.ClimatisationStatus.ClimatisationState)
if state == "" {

View file

@ -12,15 +12,15 @@ import (
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/provider"
"github.com/thoas/go-funk"
"github.com/samber/lo"
)
// Provider implements the evcc vehicle api
type Provider struct {
chargerG func() (interface{}, error)
statusG func() (interface{}, error)
climateG func() (interface{}, error)
positionG func() (interface{}, error)
chargerG func() (ChargerResponse, error)
statusG func() (StatusResponse, error)
climateG func() (ClimaterResponse, error)
positionG func() (PositionResponse, error)
action func(action, value string) error
rr func() (RolesRights, error)
}
@ -28,18 +28,18 @@ type Provider struct {
// NewProvider provides the evcc vehicle api provider
func NewProvider(api *API, vin string, cache time.Duration) *Provider {
impl := &Provider{
chargerG: provider.NewCached(func() (interface{}, error) {
chargerG: provider.Cached(func() (ChargerResponse, error) {
return api.Charger(vin)
}, cache).InterfaceGetter(),
statusG: provider.NewCached(func() (interface{}, error) {
}, cache),
statusG: provider.Cached(func() (StatusResponse, error) {
return api.Status(vin)
}, cache).InterfaceGetter(),
climateG: provider.NewCached(func() (interface{}, error) {
}, cache),
climateG: provider.Cached(func() (ClimaterResponse, error) {
return api.Climater(vin)
}, cache).InterfaceGetter(),
positionG: provider.NewCached(func() (interface{}, error) {
}, cache),
positionG: provider.Cached(func() (PositionResponse, error) {
return api.Position(vin)
}, cache).InterfaceGetter(),
}, cache),
action: func(action, value string) error {
return api.Action(vin, action, value)
},
@ -55,7 +55,7 @@ var _ api.Battery = (*Provider)(nil)
// SoC implements the api.Vehicle interface
func (v *Provider) SoC() (float64, error) {
res, err := v.chargerG()
if res, ok := res.(ChargerResponse); err == nil && ok {
if err == nil {
return float64(res.Charger.Status.BatteryStatusData.StateOfCharge.Content), nil
}
return 0, err
@ -68,7 +68,7 @@ func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.chargerG()
if res, ok := res.(ChargerResponse); err == nil && ok {
if err == nil {
if res.Charger.Status.PlugStatusData.PlugState.Content == "connected" {
status = api.StatusB
}
@ -85,7 +85,7 @@ var _ api.VehicleFinishTimer = (*Provider)(nil)
// FinishTime implements the api.VehicleFinishTimer interface
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.chargerG()
if res, ok := res.(ChargerResponse); err == nil && ok {
if err == nil {
rct := res.Charger.Status.BatteryStatusData.RemainingChargingTime
// estimate not available
@ -104,7 +104,7 @@ var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (rng int64, err error) {
res, err := v.chargerG()
if res, ok := res.(ChargerResponse); err == nil && ok {
if err == nil {
crsd := res.Charger.Status.CruisingRangeStatusData
rng = int64(crsd.PrimaryEngineRange.Content)
@ -121,7 +121,7 @@ var _ api.VehicleOdometer = (*Provider)(nil)
// Odometer implements the api.VehicleOdometer interface
func (v *Provider) Odometer() (float64, error) {
res, err := v.statusG()
if res, ok := res.(StatusResponse); err == nil && ok {
if err == nil {
err = api.ErrNotAvailable
if sd := res.ServiceByID(ServiceOdometer); sd != nil {
@ -141,7 +141,7 @@ var _ api.VehicleClimater = (*Provider)(nil)
// Climater implements the api.VehicleClimater interface
func (v *Provider) Climater() (active bool, outsideTemp float64, targetTemp float64, err error) {
res, err := v.climateG()
if res, ok := res.(ClimaterResponse); err == nil && ok {
if err == nil {
state := strings.ToLower(res.Climater.Status.ClimatisationStatusData.ClimatisationState.Content)
active := state != "off" && state != "invalid" && state != "error"
@ -162,7 +162,7 @@ var _ api.VehiclePosition = (*Provider)(nil)
// Position implements the api.VehiclePosition interface
func (v *Provider) Position() (float64, float64, error) {
res, err := v.positionG()
if res, ok := res.(PositionResponse); err == nil && ok {
if err == nil {
coord := res.FindCarResponse.Position.CarCoordinate
return float64(coord.Latitude) / 1e6, float64(coord.Longitude) / 1e6, nil
}
@ -204,14 +204,14 @@ func (v *Provider) Diagnose2() {
}
// list remaining service
services := funk.Map(rr.OperationList.ServiceInfo, func(si ServiceInfo) string {
services := lo.Map(rr.OperationList.ServiceInfo, func(si ServiceInfo, _ int) string {
if si.InvocationUrl.Content == "" {
return si.ServiceId
}
return ""
}).([]string)
})
services = funk.FilterString(services, func(s string) bool {
services = lo.Filter(services, func(s string, _ int) bool {
return s != ""
})