Powerwall: revert unification with tesla identity
This commit is contained in:
parent
4d0c46ef13
commit
a74098382a
4 changed files with 67 additions and 71 deletions
1
go.mod
1
go.mod
|
|
@ -17,6 +17,7 @@ require (
|
|||
github.com/basgys/goxml2json v1.1.0
|
||||
github.com/basvdlei/gotsmart v0.0.3
|
||||
github.com/benbjohnson/clock v1.3.5
|
||||
github.com/bogosj/tesla v1.3.1
|
||||
github.com/cenkalti/backoff/v4 v4.2.1
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/containrrr/shoutrrr v0.8.0
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -74,6 +74,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
|
|||
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bogosj/tesla v1.3.1 h1:2iTieXh8jqG8FGWs63aQfRWbD7toLUm6fQ7677eevIE=
|
||||
github.com/bogosj/tesla v1.3.1/go.mod h1:JvkjZAswd288BeRYd37fKqyYKLcDj3E5oY/OuLzndaI=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
|
|
|
|||
|
|
@ -11,14 +11,11 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/andig/go-powerwall"
|
||||
"github.com/bogosj/tesla"
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/provider"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
"github.com/evcc-io/evcc/util/sponsor"
|
||||
"github.com/evcc-io/evcc/vehicle"
|
||||
"github.com/evcc-io/evcc/vehicle/tesla"
|
||||
teslaclient "github.com/evcc-io/tesla-proxy-client"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
|
|
@ -27,7 +24,7 @@ type PowerWall struct {
|
|||
usage string
|
||||
client *powerwall.Client
|
||||
meterG func() (map[string]powerwall.MeterAggregatesData, error)
|
||||
energySite *teslaclient.EnergySite
|
||||
energySite *tesla.EnergySite
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -42,7 +39,7 @@ func NewPowerWallFromConfig(other map[string]interface{}) (api.Meter, error) {
|
|||
cc := struct {
|
||||
URI, Usage, User, Password string
|
||||
Cache time.Duration
|
||||
Tokens vehicle.Tokens
|
||||
RefreshToken string
|
||||
SiteId int64
|
||||
battery `mapstructure:",squash"`
|
||||
}{
|
||||
|
|
@ -73,15 +70,12 @@ func NewPowerWallFromConfig(other map[string]interface{}) (api.Meter, error) {
|
|||
cc.Usage = "solar"
|
||||
}
|
||||
|
||||
return NewPowerWall(cc.URI, cc.Usage, cc.User, cc.Password, cc.Cache, cc.Tokens, cc.SiteId, cc.battery)
|
||||
return NewPowerWall(cc.URI, cc.Usage, cc.User, cc.Password, cc.Cache, cc.RefreshToken, cc.SiteId, cc.battery)
|
||||
}
|
||||
|
||||
// NewPowerWall creates a Tesla PowerWall Meter
|
||||
func NewPowerWall(uri, usage, user, password string, cache time.Duration, tokens vehicle.Tokens, siteId int64, battery battery) (api.Meter, error) {
|
||||
log := util.NewLogger("powerwall").Redact(
|
||||
user, password,
|
||||
tokens.Access, tokens.Refresh,
|
||||
)
|
||||
func NewPowerWall(uri, usage, user, password string, cache time.Duration, refreshToken string, siteId int64, battery battery) (api.Meter, error) {
|
||||
log := util.NewLogger("powerwall").Redact(user, password, refreshToken)
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: request.NewTripper(log, powerwall.DefaultTransport()),
|
||||
|
|
@ -99,6 +93,50 @@ func NewPowerWall(uri, usage, user, password string, cache time.Duration, tokens
|
|||
meterG: provider.Cached(client.GetMetersAggregates, cache),
|
||||
}
|
||||
|
||||
var batteryControl bool
|
||||
if refreshToken != "" || siteId != 0 {
|
||||
if refreshToken == "" {
|
||||
return nil, errors.New("missing refresh token")
|
||||
}
|
||||
batteryControl = true
|
||||
}
|
||||
|
||||
if batteryControl {
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, request.NewClient(log))
|
||||
|
||||
options := []tesla.ClientOption{tesla.WithToken(&oauth2.Token{
|
||||
RefreshToken: refreshToken,
|
||||
Expiry: time.Now(),
|
||||
})}
|
||||
|
||||
cloudClient, err := tesla.NewClient(ctx, options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if siteId == 0 {
|
||||
// auto detect energy site ID, picking first
|
||||
products, err := cloudClient.Products()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, p := range products {
|
||||
if p.EnergySiteId != 0 {
|
||||
siteId = p.EnergySiteId
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Redact(strconv.FormatInt(siteId, 10))
|
||||
energySite, err := cloudClient.EnergySite(siteId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.energySite = energySite
|
||||
}
|
||||
|
||||
// decorate api.MeterEnergy
|
||||
var totalEnergy func() (float64, error)
|
||||
if m.usage == "load" || m.usage == "solar" {
|
||||
|
|
@ -123,51 +161,7 @@ func NewPowerWall(uri, usage, user, password string, cache time.Duration, tokens
|
|||
|
||||
// decorate api.BatteryController
|
||||
var batModeS func(api.BatteryMode) error
|
||||
|
||||
token, err := tokens.Token()
|
||||
if err == nil {
|
||||
if !sponsor.IsAuthorized() {
|
||||
return nil, api.ErrSponsorRequired
|
||||
}
|
||||
|
||||
identity, err := tesla.NewIdentity(log, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hc := request.NewClient(log)
|
||||
hc.Transport = &oauth2.Transport{
|
||||
Source: identity,
|
||||
Base: hc.Transport,
|
||||
}
|
||||
|
||||
cloudClient, err := teslaclient.NewClient(context.Background(), teslaclient.WithClient(hc))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if siteId == 0 {
|
||||
// auto detect energy site ID, picking first
|
||||
products, err := cloudClient.Products()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, p := range products {
|
||||
if p.EnergySiteId != 0 {
|
||||
siteId = p.EnergySiteId
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Redact(strconv.FormatInt(siteId, 10))
|
||||
energySite, err := cloudClient.EnergySite(siteId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.energySite = energySite
|
||||
|
||||
if batteryControl {
|
||||
batModeS = battery.LimitController(m.socG, func(limit float64) error {
|
||||
return m.energySite.SetBatteryReserve(uint64(limit))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,14 +7,19 @@ capabilities: ["battery-control"]
|
|||
requirements:
|
||||
description:
|
||||
de: |
|
||||
Benötigt `access` und `refresh` Tokens. Diese können über [tesla.evcc.io](https://tesla.evcc.io) erstellt werden.
|
||||
Die optionale Batteriesteuerung erfolgt über einen evcc Proxy-Server und erfordert ein Sponsor Token.
|
||||
Siehe https://tesla.evcc.io.
|
||||
Um die optionale Entladesteuerung der Battery zu nutzen wird ein `refresh` Token für die Kommunikation mit der Tesla API benötigt.
|
||||
|
||||
Folgende Apps ermöglichen das Erstellen des Tokens:
|
||||
- [Auth app for Tesla (iOS)](https://apps.apple.com/us/app/auth-app-for-tesla/id1552058613#?platform=iphone)
|
||||
- [Tesla Tokens (Android)](https://play.google.com/store/apps/details?id=net.leveugle.teslatokens)
|
||||
- [Tesla Auth (macOS, Linux)](https://github.com/adriankumpf/tesla_auth)
|
||||
en: |
|
||||
Teska `access` and `refresh` tokens are required. These can be generated through [tesla.evcc.io](https://tesla.evcc.io).
|
||||
Optional battery control is done via an evcc proxy server and requires a sponsor token.
|
||||
Siehe https://tesla.evcc.io.
|
||||
evcc: ["sponsorship"]
|
||||
To use the optional battery control you need to generate a `refresh` token for communicating with the Tesla API.
|
||||
|
||||
The following apps allow to create the token:
|
||||
- [Auth app for Tesla (iOS)](https://apps.apple.com/us/app/auth-app-for-tesla/id1552058613#?platform=iphone)
|
||||
- [Tesla Tokens (Android)](https://play.google.com/store/apps/details?id=net.leveugle.teslatokens)
|
||||
- [Tesla Auth (macOS, Linux)](https://github.com/adriankumpf/tesla_auth)
|
||||
params:
|
||||
- name: usage
|
||||
choice: ["grid", "pv", "battery"]
|
||||
|
|
@ -25,10 +30,6 @@ params:
|
|||
help:
|
||||
en: Password of the user "customer". By default this is the last 5 characters of password stated on the Tesla Gateway.
|
||||
de: Passwort des Benutzers "Kunde". Default sind die letzten 5 Zeichen des auf dem Tesla Gateway genannten Passworts.
|
||||
- name: accessToken
|
||||
help:
|
||||
en: "See https://docs.evcc.io/en/docs/devices/meters#tesla-powerwall"
|
||||
de: "Siehe https://docs.evcc.io/docs/devices/meters#tesla-powerwall"
|
||||
- name: refreshToken
|
||||
help:
|
||||
en: "See https://docs.evcc.io/en/docs/devices/meters#tesla-powerwall"
|
||||
|
|
@ -55,9 +56,7 @@ render: |
|
|||
usage: {{ .usage }}
|
||||
user: customer
|
||||
password: {{ .password }} # for user 'customer'
|
||||
tokens:
|
||||
access: {{ .accessToken }}
|
||||
refresh: {{ .refreshToken }}
|
||||
refreshToken: {{ .refreshToken }}
|
||||
siteId: {{ .siteId }}
|
||||
minSoc: {{ .minSoc }}
|
||||
maxSoc: {{ .maxSoc }}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue