Simplify powerwall login

This commit is contained in:
andig 2021-03-09 18:05:02 +01:00
parent 15a0648cb0
commit 84eecff4a1
2 changed files with 50 additions and 48 deletions

View file

@ -0,0 +1,31 @@
package powerwall
// credits to https://github.com/vloschiavo/powerwall2
// URIs
const (
MeterURI = "/api/meters/aggregates"
BatteryURI = "/api/system_status/soe"
LoginURI = "/api/login/Basic"
)
// MeterResponse is the /api/system_status/aggregates response
type MeterResponse map[string]struct {
LastCommunicationTime string `json:"last_communication_time"`
InstantPower float64 `json:"instant_power"`
InstantReactivePower float64 `json:"instant_reactive_power"`
InstantApparentPower float64 `json:"instant_apparent_power"`
Frequency float64 `json:"frequency"`
EnergyExported float64 `json:"energy_exported"`
EnergyImported float64 `json:"energy_imported"`
InstantAverageVoltage float64 `json:"instant_average_voltage"`
InstantTotalCurrent float64 `json:"instant_total_current"`
IACurrent float64 `json:"i_a_current"`
IBCurrent float64 `json:"i_b_current"`
ICCurrent float64 `json:"i_c_current"`
}
// BatteryResponse is the /api/system_status/soe response
type BatteryResponse struct {
Percentage float64 `json:"percentage"`
}

View file

@ -1,46 +1,21 @@
package meter
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"github.com/andig/evcc/api"
"github.com/andig/evcc/meter/powerwall"
"github.com/andig/evcc/util"
"github.com/andig/evcc/util/request"
)
// credits to https://github.com/vloschiavo/powerwall2
const (
teslaMeterURI = "/api/meters/aggregates"
teslaBatteryURI = "/api/system_status/soe"
teslaLoginURI = "/api/login/Basic"
)
type teslaMeterResponse map[string]struct {
LastCommunicationTime string `json:"last_communication_time"`
InstantPower float64 `json:"instant_power"`
InstantReactivePower float64 `json:"instant_reactive_power"`
InstantApparentPower float64 `json:"instant_apparent_power"`
Frequency float64 `json:"frequency"`
EnergyExported float64 `json:"energy_exported"`
EnergyImported float64 `json:"energy_imported"`
InstantAverageVoltage float64 `json:"instant_average_voltage"`
InstantTotalCurrent float64 `json:"instant_total_current"`
IACurrent float64 `json:"i_a_current"`
IBCurrent float64 `json:"i_b_current"`
ICCurrent float64 `json:"i_c_current"`
}
type teslaBatteryResponse struct {
Percentage float64 `json:"percentage"`
}
// Tesla is the tesla powerwall meter
type Tesla struct {
*request.Helper
@ -93,7 +68,7 @@ func NewTesla(uri, usage, password string) (api.Meter, error) {
m := &Tesla{
Helper: request.NewHelper(log),
uri: util.DefaultScheme(uri, "https"),
uri: util.DefaultScheme(strings.TrimSuffix(uri, "/"), "https"),
usage: strings.ToLower(usage),
password: password,
}
@ -124,30 +99,26 @@ func NewTesla(uri, usage, password string) (api.Meter, error) {
// Login calls login and saves the returned cookie
func (m *Tesla) Login() error {
// username for the powerwall seems to always be customer; email is not required for authentication
payload := map[string]interface{}{"password": m.password, "username": "customer"}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
data := map[string]interface{}{
"username": "customer",
"password": m.password,
}
// returns cookie which is saved in the cookie jar
resp, err := m.Client.Post(m.uri+teslaLoginURI, "application/json", bytes.NewReader(payloadBytes))
if err != nil {
return err
req, err := request.New(http.MethodPost, m.uri+powerwall.LoginURI, request.MarshalJSON(data), request.JSONEncoding)
if err == nil {
// use DoBody as it will close the response body
if _, err = m.DoBody(req); err != nil {
err = fmt.Errorf("login failed: %w", err)
}
}
if body, err := request.ReadBody(resp); err != nil {
return fmt.Errorf("couldn't login: %s: %s", err, string(body))
}
return nil
return err
}
// CurrentPower implements the Meter.CurrentPower interface
func (m *Tesla) CurrentPower() (float64, error) {
var res teslaMeterResponse
if err := m.GetJSON(m.uri+teslaMeterURI, &res); err != nil {
var res powerwall.MeterResponse
if err := m.GetJSON(m.uri+powerwall.MeterURI, &res); err != nil {
return 0, err
}
@ -160,8 +131,8 @@ func (m *Tesla) CurrentPower() (float64, error) {
// totalEnergy implements the api.MeterEnergy interface
func (m *Tesla) totalEnergy() (float64, error) {
var res teslaMeterResponse
if err := m.GetJSON(m.uri+teslaMeterURI, &res); err != nil {
var res powerwall.MeterResponse
if err := m.GetJSON(m.uri+powerwall.MeterURI, &res); err != nil {
return 0, err
}
@ -179,8 +150,8 @@ func (m *Tesla) totalEnergy() (float64, error) {
// batterySoC implements the api.Battery interface
func (m *Tesla) batterySoC() (float64, error) {
var res teslaBatteryResponse
err := m.GetJSON(m.uri+teslaBatteryURI, &res)
var res powerwall.BatteryResponse
err := m.GetJSON(m.uri+powerwall.BatteryURI, &res)
return res.Percentage, err
}