Add Tesla Powerwall (#92)

This commit is contained in:
andig 2020-05-02 21:33:58 +02:00 • committed by GitHub
parent eeef3f1fe8
commit 500f6fe80e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 133 additions and 2 deletions

View file

@ -8,7 +8,7 @@ EVCC is an extensible EV Charge Controller with PV integration implemented in [G
- simple and clean user interface
- multiple [chargers](#charger): Wallbe, Phoenix, go-eCharger, NRGKick, SimpleEVSE, EVSEWifi, KEBA/BMW, openWB, Mobile Charger Connect, any other charger using scripting
- multiple [meters](#meter): ModBus (SDM630, MPM3PM, SBC ALE3 and many more), SMA Home Manager and SMA Energy Meter, KOSTAL Smart Energy Meter (KSEM, EMxx), any Sunspec-compatible inverter or home battery devices (Fronius, SMA, SolarEdge, KOSTAL, STECA, E3DC)
- multiple [meters](#meter): ModBus (SDM630, MPM3PM, SBC ALE3 and many more), SMA Home Manager and SMA Energy Meter, KOSTAL Smart Energy Meter (KSEM, EMxx), any Sunspec-compatible inverter or home battery devices (Fronius, SMA, SolarEdge, KOSTAL, STECA, E3DC, Tesla PowerWall)
- different [vehicles](#vehicle) to show battery status: Audi (eTron), BMW (i3), Tesla, Nissan (Leaf), any other vehicle using scripting
- [plugins](#plugins) for integrating with hardware devices and home automation: Modbus (meters and grid inverters), MQTT and shell scripts
- status notifications using [Telegram](https://telegram.org) and [PushOver](https://pushover.net)
@ -209,7 +209,7 @@ Meters provide data about power and energy consumption. Available meter implemen
energy: Export # optional reading for total energy values, specify for charge meter
```
- `sma`: SMA Home Manager and SMA Energy Meter. Power reading is configured out of the box but can be customizied if necessary. To obtain energy readings define the desired Obis code (Import Energy: "1:1.8.0", Export Energy: "1:2.8.0"):
- `sma`: SMA Home Manager and SMA Energy Meter. Power reading is configured out of the box but can be customized if necessary. To obtain energy readings define the desired Obis code (Import Energy: "1:1.8.0", Export Energy: "1:2.8.0"):
```yaml
- name: sma-home-manager
@ -219,6 +219,17 @@ Meters provide data about power and energy consumption. Available meter implemen
energy: # leave empty to disable or choose obis 1:1.8.0/1:2.8.0
```
- `tesla`: Tesla PowerWall meter. Use `value` to choose meter (grid meter: `site`, pv: `solar`, battery: `battery`)
```yaml
- name: powerwall
type: tesla
uri: http://192.168.1.4/api/meters/aggregates
meter: site # grid meter: `site`, pv: `solar`, battery: `battery`
```
*Note*: this could also be implemented using a `default` meter with the `http` plugin.
- `default`: default meter implementation where meter readings- `power` and `energy` are configured using [plugin](#plugins)
### Vehicle

View file

@ -18,6 +18,8 @@ func NewFromConfig(log *util.Logger, typ string, other map[string]interface{}) a
c = NewModbusFromConfig(log, other)
case "sma":
c = NewSMAFromConfig(log, other)
case "tesla", "powerwall":
c = NewTeslaFromConfig(log, other)
default:
log.FATAL.Fatalf("invalid meter type '%s'", typ)
}

118
meter/tesla.go Normal file
View file

@ -0,0 +1,118 @@
package meter
import (
"crypto/tls"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/andig/evcc/api"
"github.com/andig/evcc/util"
)
// credits to https://github.com/vloschiavo/powerwall2
type teslaResponse 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"`
}
// Tesla is the tesla powerwall meter
type Tesla struct {
*util.HTTPHelper
uri, usage string
}
// NewTeslaFromConfig creates a Tesla Powerwall Meter from generic config
func NewTeslaFromConfig(log *util.Logger, other map[string]interface{}) api.Meter {
cc := struct {
URI, Usage string
}{}
util.DecodeOther(log, other, &cc)
if cc.Usage == "" {
log.FATAL.Fatalf("config: missing usage")
}
url, err := url.ParseRequestURI(cc.URI)
if err != nil {
log.FATAL.Fatalf("config: invalid uri %s", cc.URI)
}
if url.Path == "" {
url.Path = "api/meters/aggregates"
cc.URI = url.String()
}
return NewTesla(cc.URI, cc.Usage)
}
// NewTesla creates a Tesla Meter
func NewTesla(uri, usage string) api.Meter {
m := &Tesla{
HTTPHelper: util.NewHTTPHelper(util.NewLogger("tsla")),
uri: uri,
usage: strings.ToLower(usage),
}
// ignore the self signed certificate
customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
m.HTTPHelper.Client.Transport = customTransport
// decorate api.MeterEnergy
if m.usage == "load" || m.usage == "solar" {
return &TeslaEnergy{Tesla: m}
}
return m
}
// CurrentPower implements the Meter.CurrentPower interface
func (m *Tesla) CurrentPower() (float64, error) {
var tr teslaResponse
_, err := m.GetJSON(m.uri, &tr)
if err == nil {
if o, ok := tr[m.usage]; ok {
return o.InstantPower, nil
}
}
return 0, fmt.Errorf("invalid usage: %s", m.usage)
}
// TeslaEnergy decorates Tesla with api.MeterEnergy interface
type TeslaEnergy struct {
*Tesla
}
// TotalEnergy implements the api.MeterEnergy interface
func (m *TeslaEnergy) TotalEnergy() (float64, error) {
var tr teslaResponse
_, err := m.GetJSON(m.uri, &tr)
if err == nil {
if o, ok := tr[m.usage]; ok {
if m.usage == "load" {
return o.EnergyImported, nil
}
if m.usage == "solar" {
return o.EnergyExported, nil
}
}
}
return 0, fmt.Errorf("invalid usage: %s", m.usage)
}