Require Go 1.18 and use generics (#2116)
This commit is contained in:
parent
cf3b6fb2a2
commit
1bfd0f6355
63 changed files with 467 additions and 788 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package charger
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !gokrazy
|
||||
// +build !gokrazy
|
||||
|
||||
package cmd
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build gokrazy
|
||||
// +build gokrazy
|
||||
|
||||
package cmd
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build gokrazy
|
||||
// +build gokrazy
|
||||
|
||||
package cmd
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package cmd
|
||||
|
||||
|
|
|
|||
15
cmd/token.go
15
cmd/token.go
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
10
go.mod
|
|
@ -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
17
go.sum
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !release
|
||||
// +build !release
|
||||
|
||||
package server
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package server
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package server
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build gokrazy
|
||||
// +build gokrazy
|
||||
|
||||
package updater
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !gokrazy
|
||||
// +build !gokrazy
|
||||
|
||||
package updater
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build gokrazy
|
||||
// +build gokrazy
|
||||
|
||||
package updater
|
||||
|
||||
|
|
|
|||
1
tools.go
1
tools.go
|
|
@ -1,5 +1,4 @@
|
|||
//go:build tools
|
||||
// +build tools
|
||||
|
||||
package main
|
||||
|
||||
|
|
|
|||
|
|
@ -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__
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 == "" {
|
||||
|
|
|
|||
|
|
@ -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 != ""
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue