Return errors instead of failing
This commit is contained in:
parent
9e7cf67128
commit
b1de0950c2
51 changed files with 635 additions and 418 deletions
|
|
@ -1,6 +1,8 @@
|
|||
package charger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/andig/evcc/api"
|
||||
"github.com/andig/evcc/provider"
|
||||
"github.com/andig/evcc/util"
|
||||
|
|
@ -15,9 +17,11 @@ type Charger struct {
|
|||
}
|
||||
|
||||
// NewConfigurableFromConfig creates a new configurable charger
|
||||
func NewConfigurableFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
func NewConfigurableFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := struct{ Status, Enable, Enabled, MaxCurrent provider.Config }{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range map[string]string{
|
||||
"status": cc.Status.Type,
|
||||
|
|
@ -26,18 +30,32 @@ func NewConfigurableFromConfig(log *util.Logger, other map[string]interface{}) a
|
|||
"maxcurrent": cc.MaxCurrent.Type,
|
||||
} {
|
||||
if v == "" {
|
||||
log.FATAL.Fatalf("default charger config: %s required", k)
|
||||
return nil, fmt.Errorf("default charger config: %s required", k)
|
||||
}
|
||||
}
|
||||
|
||||
charger := NewConfigurable(
|
||||
provider.NewStringGetterFromConfig(log, cc.Status),
|
||||
provider.NewBoolGetterFromConfig(log, cc.Enabled),
|
||||
provider.NewBoolSetterFromConfig(log, "enable", cc.Enable),
|
||||
provider.NewIntSetterFromConfig(log, "maxcurrent", cc.MaxCurrent),
|
||||
)
|
||||
status, err := provider.NewStringGetterFromConfig(cc.Status)
|
||||
|
||||
return charger
|
||||
var enabled func() (bool, error)
|
||||
if err == nil {
|
||||
enabled, err = provider.NewBoolGetterFromConfig(cc.Enabled)
|
||||
}
|
||||
|
||||
var enable func(bool) error
|
||||
if err == nil {
|
||||
enable, err = provider.NewBoolSetterFromConfig("enable", cc.Enable)
|
||||
}
|
||||
|
||||
var maxcurrent func(int64) error
|
||||
if err == nil {
|
||||
maxcurrent, err = provider.NewIntSetterFromConfig("maxcurrent", cc.MaxCurrent)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewConfigurable(status, enabled, enable, maxcurrent)
|
||||
}
|
||||
|
||||
// NewConfigurable creates a new charger
|
||||
|
|
@ -46,13 +64,15 @@ func NewConfigurable(
|
|||
enabledG func() (bool, error),
|
||||
enableS func(bool) error,
|
||||
maxCurrentS func(int64) error,
|
||||
) api.Charger {
|
||||
return &Charger{
|
||||
) (api.Charger, error) {
|
||||
c := &Charger{
|
||||
statusG: statusG,
|
||||
enabledG: enabledG,
|
||||
enableS: enableS,
|
||||
maxCurrentS: maxCurrentS,
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Status implements the Charger.Status interface
|
||||
|
|
|
|||
|
|
@ -1,44 +1,42 @@
|
|||
package charger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/andig/evcc/api"
|
||||
"github.com/andig/evcc/util"
|
||||
)
|
||||
|
||||
type apiFunction string
|
||||
|
||||
// NewFromConfig creates charger from configuration
|
||||
func NewFromConfig(log *util.Logger, typ string, other map[string]interface{}) api.Charger {
|
||||
var c api.Charger
|
||||
|
||||
func NewFromConfig(typ string, other map[string]interface{}) (charger api.Charger, err error) {
|
||||
switch strings.ToLower(typ) {
|
||||
case "default", "configurable":
|
||||
c = NewConfigurableFromConfig(log, other)
|
||||
charger, err = NewConfigurableFromConfig(other)
|
||||
case "wallbe":
|
||||
c = NewWallbeFromConfig(log, other)
|
||||
charger, err = NewWallbeFromConfig(other)
|
||||
case "phoenix-emcp":
|
||||
c = NewPhoenixEMCPFromConfig(log, other)
|
||||
charger, err = NewPhoenixEMCPFromConfig(other)
|
||||
case "phoenix-evcc":
|
||||
c = NewPhoenixEVCCFromConfig(log, other)
|
||||
charger, err = NewPhoenixEVCCFromConfig(other)
|
||||
case "nrgkick-bluetooth", "nrgkick-bt", "nrgble":
|
||||
c = NewNRGKickBLEFromConfig(log, other)
|
||||
charger, err = NewNRGKickBLEFromConfig(other)
|
||||
case "nrgkick-connect", "nrgconnect":
|
||||
c = NewNRGKickConnectFromConfig(log, other)
|
||||
charger, err = NewNRGKickConnectFromConfig(other)
|
||||
case "go-e", "goe":
|
||||
c = NewGoEFromConfig(log, other)
|
||||
charger, err = NewGoEFromConfig(other)
|
||||
case "evsewifi":
|
||||
c = NewEVSEWifiFromConfig(log, other)
|
||||
charger, err = NewEVSEWifiFromConfig(other)
|
||||
case "simpleevse", "evse":
|
||||
c = NewSimpleEVSEFromConfig(log, other)
|
||||
charger, err = NewSimpleEVSEFromConfig(other)
|
||||
case "porsche", "audi", "bentley", "mcc":
|
||||
c = NewMobileConnectFromConfig(log, other)
|
||||
charger, err = NewMobileConnectFromConfig(other)
|
||||
case "keba", "bmw":
|
||||
c = NewKebaFromConfig(log, other)
|
||||
charger, err = NewKebaFromConfig(other)
|
||||
default:
|
||||
log.FATAL.Fatalf("invalid charger type '%s'", typ)
|
||||
return nil, fmt.Errorf("invalid charger type: %s", typ)
|
||||
}
|
||||
|
||||
return c
|
||||
return charger, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,21 +50,23 @@ type EVSEWifi struct {
|
|||
}
|
||||
|
||||
// NewEVSEWifiFromConfig creates a EVSEWifi charger from generic config
|
||||
func NewEVSEWifiFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
func NewEVSEWifiFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := struct{ URI string }{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewEVSEWifi(cc.URI)
|
||||
}
|
||||
|
||||
// NewEVSEWifi creates EVSEWifi charger
|
||||
func NewEVSEWifi(uri string) api.Charger {
|
||||
func NewEVSEWifi(uri string) (api.Charger, error) {
|
||||
evse := &EVSEWifi{
|
||||
HTTPHelper: util.NewHTTPHelper(util.NewLogger("wifi")),
|
||||
uri: strings.TrimRight(uri, "/"),
|
||||
}
|
||||
|
||||
return evse
|
||||
return evse, nil
|
||||
}
|
||||
|
||||
func (evse *EVSEWifi) apiURL(service apiFunction) string {
|
||||
|
|
|
|||
|
|
@ -45,33 +45,35 @@ type GoE struct {
|
|||
}
|
||||
|
||||
// NewGoEFromConfig creates a go-e charger from generic config
|
||||
func NewGoEFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
func NewGoEFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := struct {
|
||||
Token string
|
||||
URI string
|
||||
Cache time.Duration
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cc.URI != "" && cc.Token != "" {
|
||||
log.FATAL.Fatal("config: should only have one of uri/token")
|
||||
return nil, errors.New("go-e config: should only have one of uri/token")
|
||||
}
|
||||
if cc.URI == "" && cc.Token == "" {
|
||||
log.FATAL.Fatal("config: must have one of uri/token")
|
||||
return nil, errors.New("go-e config: must have one of uri/token")
|
||||
}
|
||||
|
||||
return NewGoE(cc.URI, cc.Token, cc.Cache)
|
||||
}
|
||||
|
||||
// NewGoE creates GoE charger
|
||||
func NewGoE(uri, token string, cache time.Duration) *GoE {
|
||||
func NewGoE(uri, token string, cache time.Duration) (*GoE, error) {
|
||||
c := &GoE{
|
||||
HTTPHelper: util.NewHTTPHelper(util.NewLogger("go-e")),
|
||||
uri: strings.TrimRight(uri, "/"),
|
||||
token: strings.TrimSpace(token),
|
||||
}
|
||||
|
||||
return c
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *GoE) localResponse(function, payload string) (goeStatusResponse, error) {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@ import (
|
|||
|
||||
// TestGoE tests interfaces
|
||||
func TestGoE(t *testing.T) {
|
||||
var wb api.Charger = NewGoE("foo", "bar", 0)
|
||||
var wb api.Charger
|
||||
wb, err := NewGoE("foo", "bar", 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if _, ok := wb.(api.MeterCurrent); !ok {
|
||||
t.Error("missing MeterCurrent interface")
|
||||
|
|
@ -37,19 +37,21 @@ type Keba struct {
|
|||
}
|
||||
|
||||
// NewKebaFromConfig creates a new configurable charger
|
||||
func NewKebaFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
func NewKebaFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := struct {
|
||||
URI string
|
||||
Timeout time.Duration
|
||||
RFID RFID
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewKeba(cc.URI, cc.RFID, cc.Timeout)
|
||||
}
|
||||
|
||||
// NewKeba creates a new charger
|
||||
func NewKeba(conn string, rfid RFID, timeout time.Duration) api.Charger {
|
||||
func NewKeba(conn string, rfid RFID, timeout time.Duration) (api.Charger, error) {
|
||||
log := util.NewLogger("keba")
|
||||
|
||||
if keba.Instance == nil {
|
||||
|
|
@ -75,7 +77,7 @@ func NewKeba(conn string, rfid RFID, timeout time.Duration) api.Charger {
|
|||
|
||||
keba.Instance.Subscribe(conn, c.recv)
|
||||
|
||||
return c
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Keba) send(msg string) error {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ import (
|
|||
)
|
||||
|
||||
func TestKeba(t *testing.T) {
|
||||
var wb api.Charger = NewKeba("foo", RFID{}, 0)
|
||||
var wb api.Charger
|
||||
wb, err := NewKeba("foo", RFID{}, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if _, ok := wb.(api.MeterEnergy); !ok {
|
||||
t.Error("missing MeterEnergy interface")
|
||||
|
|
|
|||
|
|
@ -68,17 +68,19 @@ type MobileConnect struct {
|
|||
}
|
||||
|
||||
// NewMobileConnectFromConfig creates a MCC charger from generic config
|
||||
func NewMobileConnectFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
func NewMobileConnectFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := struct{ URI, Password string }{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewMobileConnect(cc.URI, cc.Password)
|
||||
}
|
||||
|
||||
// NewMobileConnect creates MCC charger
|
||||
func NewMobileConnect(uri string, password string) *MobileConnect {
|
||||
func NewMobileConnect(uri string, password string) (*MobileConnect, error) {
|
||||
mcc := &MobileConnect{
|
||||
HTTPHelper: util.NewHTTPHelper(util.NewLogger("mcc ")),
|
||||
HTTPHelper: util.NewHTTPHelper(util.NewLogger("mcc")),
|
||||
uri: strings.TrimRight(uri, "/"),
|
||||
password: password,
|
||||
}
|
||||
|
|
@ -89,7 +91,7 @@ func NewMobileConnect(uri string, password string) *MobileConnect {
|
|||
|
||||
mcc.HTTPHelper.Client.Transport = customTransport
|
||||
|
||||
return mcc
|
||||
return mcc, nil
|
||||
}
|
||||
|
||||
// construct the URL for a given apiFunction
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
package charger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/andig/evcc/api"
|
||||
"github.com/andig/evcc/util"
|
||||
)
|
||||
|
||||
// NewNRGKickBLEFromConfig creates a NRGKickBLE charger from generic config
|
||||
func NewNRGKickBLEFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
log.FATAL.Fatal("config: NRGKick bluetooth is only supported on linux")
|
||||
return nil
|
||||
func NewNRGKickBLEFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
return nil, errors.New("NRGKick bluetooth is only supported on linux")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package charger
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
|
@ -35,23 +36,25 @@ type NRGKickBLE struct {
|
|||
}
|
||||
|
||||
// NewNRGKickBLEFromConfig creates a NRGKickBLE charger from generic config
|
||||
func NewNRGKickBLEFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
func NewNRGKickBLEFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := struct{ Device, MacAddress, PIN string }{
|
||||
Device: "hci0",
|
||||
}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// decode PIN with leading zero
|
||||
pin, err := strconv.Atoi(cc.PIN)
|
||||
if err != nil {
|
||||
log.FATAL.Fatalf("config: invalid pin '%s'", cc.PIN)
|
||||
return nil, errors.New("config: invalid pin '%s'", cc.PIN)
|
||||
}
|
||||
|
||||
return NewNRGKickBLE(cc.Device, cc.MacAddress, pin)
|
||||
}
|
||||
|
||||
// NewNRGKickBLE creates NRGKickBLE charger
|
||||
func NewNRGKickBLE(device, macaddress string, pin int) *NRGKickBLE {
|
||||
func NewNRGKickBLE(device, macaddress string, pin int) (*NRGKickBLE, error) {
|
||||
logger := util.NewLogger("nrg-bt")
|
||||
|
||||
// set LE mode
|
||||
|
|
@ -73,18 +76,18 @@ func NewNRGKickBLE(device, macaddress string, pin int) *NRGKickBLE {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
logger.FATAL.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
adapt, err := adapter.NewAdapter1FromAdapterID(device)
|
||||
if err != nil {
|
||||
logger.FATAL.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//Connect DBus System bus
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
logger.FATAL.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// do not reuse agent0 from service
|
||||
|
|
@ -93,7 +96,7 @@ func NewNRGKickBLE(device, macaddress string, pin int) *NRGKickBLE {
|
|||
ag := agent.NewSimpleAgent()
|
||||
err = agent.ExposeAgent(conn, ag, agent.CapNoInputNoOutput, true)
|
||||
if err != nil {
|
||||
logger.FATAL.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nrg := &NRGKickBLE{
|
||||
|
|
@ -106,7 +109,7 @@ func NewNRGKickBLE(device, macaddress string, pin int) *NRGKickBLE {
|
|||
agent: ag,
|
||||
}
|
||||
|
||||
return nrg
|
||||
return nrg, nil
|
||||
}
|
||||
|
||||
func (nrg *NRGKickBLE) connect() (*device.Device1, error) {
|
||||
|
|
|
|||
|
|
@ -70,17 +70,20 @@ type NRGKickConnect struct {
|
|||
}
|
||||
|
||||
// NewNRGKickConnectFromConfig creates a NRGKickConnect charger from generic config
|
||||
func NewNRGKickConnectFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
func NewNRGKickConnectFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := struct{ IP, MacAddress, Password string }{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNRGKickConnect(cc.IP, cc.MacAddress, cc.Password)
|
||||
}
|
||||
|
||||
// NewNRGKickConnect creates NRGKickConnect charger
|
||||
func NewNRGKickConnect(IP, MacAddress, Password string) *NRGKickConnect {
|
||||
func NewNRGKickConnect(IP, MacAddress, Password string) (*NRGKickConnect, error) {
|
||||
log := util.NewLogger("nrgconn")
|
||||
nrg := &NRGKickConnect{
|
||||
HTTPHelper: util.NewHTTPHelper(util.NewLogger("nrgc")),
|
||||
HTTPHelper: util.NewHTTPHelper(log),
|
||||
IP: IP,
|
||||
MacAddress: MacAddress,
|
||||
Password: Password,
|
||||
|
|
@ -88,7 +91,7 @@ func NewNRGKickConnect(IP, MacAddress, Password string) *NRGKickConnect {
|
|||
|
||||
nrg.HTTPHelper.Log.WARN.Println("-- experimental --")
|
||||
|
||||
return nrg
|
||||
return nrg, nil
|
||||
}
|
||||
|
||||
func (nrg *NRGKickConnect) apiURL(api apiFunction) string {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ import (
|
|||
)
|
||||
|
||||
func TestNRGKickConnect(t *testing.T) {
|
||||
var wb api.Charger = NewNRGKickConnect("foo", "bar", "baz")
|
||||
var wb api.Charger
|
||||
wb, err := NewNRGKickConnect("foo", "bar", "baz")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if _, ok := wb.(api.MeterEnergy); !ok {
|
||||
t.Error("missing MeterEnergy interface")
|
||||
|
|
|
|||
|
|
@ -26,27 +26,27 @@ type PhoenixEMCP struct {
|
|||
}
|
||||
|
||||
// NewPhoenixEMCPFromConfig creates a Phoenix charger from generic config
|
||||
func NewPhoenixEMCPFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
func NewPhoenixEMCPFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := struct {
|
||||
URI string
|
||||
ID uint8
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
if _, _, err := net.SplitHostPort(cc.URI); err != nil {
|
||||
log.FATAL.Printf("config: missing or invalid phoenix EM-CP uri: %s", cc.URI)
|
||||
}{
|
||||
ID: 180, // default
|
||||
}
|
||||
|
||||
if cc.ID == 0 {
|
||||
cc.ID = 180
|
||||
log.WARN.Printf("config: missing phoenix EM-CP slave id, assuming default %d", cc.ID)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, _, err := net.SplitHostPort(cc.URI); err != nil {
|
||||
return nil, fmt.Errorf("config: missing or invalid phoenix EM-CP uri: %s", cc.URI)
|
||||
}
|
||||
|
||||
return NewPhoenixEMCP(cc.URI, cc.ID)
|
||||
}
|
||||
|
||||
// NewPhoenixEMCP creates a Phoenix charger
|
||||
func NewPhoenixEMCP(uri string, id uint8) api.Charger {
|
||||
func NewPhoenixEMCP(uri string, id uint8) (api.Charger, error) {
|
||||
log := util.NewLogger("emcp")
|
||||
|
||||
handler := modbus.NewTCPClientHandler(uri)
|
||||
|
|
@ -62,7 +62,7 @@ func NewPhoenixEMCP(uri string, id uint8) api.Charger {
|
|||
handler: handler,
|
||||
}
|
||||
|
||||
return wb
|
||||
return wb, nil
|
||||
}
|
||||
|
||||
// Status implements the Charger.Status interface
|
||||
|
|
|
|||
|
|
@ -25,22 +25,24 @@ type PhoenixEVCC struct {
|
|||
}
|
||||
|
||||
// NewPhoenixEVCCFromConfig creates a Phoenix charger from generic config
|
||||
func NewPhoenixEVCCFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
var cc modbus.Connection
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
if cc.ID == 0 {
|
||||
cc.ID = 255
|
||||
log.WARN.Printf("config: missing phoenix EV-CC slave id, assuming default %d", cc.ID)
|
||||
func NewPhoenixEVCCFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := modbus.Connection{ID: 255}
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewPhoenixEVCC(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID)
|
||||
}
|
||||
|
||||
// NewPhoenixEVCC creates a Phoenix charger
|
||||
func NewPhoenixEVCC(uri, device, comset string, baudrate int, id uint8) api.Charger {
|
||||
func NewPhoenixEVCC(uri, device, comset string, baudrate int, id uint8) (api.Charger, error) {
|
||||
log := util.NewLogger("evcc")
|
||||
conn := modbus.NewConnection(log, uri, device, comset, baudrate, true)
|
||||
|
||||
conn, err := modbus.NewConnection(uri, device, comset, baudrate, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn.Slave(id)
|
||||
|
||||
wb := &PhoenixEVCC{
|
||||
|
|
@ -49,7 +51,7 @@ func NewPhoenixEVCC(uri, device, comset string, baudrate int, id uint8) api.Char
|
|||
handler: conn,
|
||||
}
|
||||
|
||||
return wb
|
||||
return wb, nil
|
||||
}
|
||||
|
||||
// Status implements the Charger.Status interface
|
||||
|
|
|
|||
|
|
@ -23,15 +23,17 @@ const (
|
|||
)
|
||||
|
||||
// NewSimpleEVSEFromConfig creates a SimpleEVSE charger from generic config
|
||||
func NewSimpleEVSEFromConfig(log *util.Logger, other map[string]interface{}) api.Charger {
|
||||
func NewSimpleEVSEFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := struct{ URI, Device string }{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewSimpleEVSE(cc.URI, cc.Device)
|
||||
}
|
||||
|
||||
// NewSimpleEVSE creates SimpleEVSE charger
|
||||
func NewSimpleEVSE(conn, device string) api.Charger {
|
||||
func NewSimpleEVSE(conn, device string) (api.Charger, error) {
|
||||
log := util.NewLogger("evse")
|
||||
|
||||
var handler modbus.ClientHandler
|
||||
|
|
@ -53,7 +55,7 @@ func NewSimpleEVSE(conn, device string) api.Charger {
|
|||
handler.(*modbus.RTUClientHandler).SlaveID = 1
|
||||
}
|
||||
if handler == nil {
|
||||
log.FATAL.Fatal("must define either uri or device")
|
||||
return nil, errors.New("must define either uri or device")
|
||||
}
|
||||
|
||||
evse := &SimpleEVSE{
|
||||
|
|
@ -64,7 +66,7 @@ func NewSimpleEVSE(conn, device string) api.Charger {
|
|||
|
||||
evse.log.WARN.Println("-- experimental --")
|
||||
|
||||
return evse
|
||||
return evse, nil
|
||||
}
|
||||
|
||||
// Status implements the Charger.Status interface
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package charger
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/andig/evcc/api"
|
||||
|
|
@ -36,16 +34,16 @@ type Wallbe struct {
|
|||
}
|
||||
|
||||
// NewWallbeFromConfig creates a Wallbe charger from generic config
|
||||
func NewWallbeFromConfig(log *util.Logger, other map[string]interface{}) *Wallbe {
|
||||
func NewWallbeFromConfig(other map[string]interface{}) (*Wallbe, error) {
|
||||
cc := struct {
|
||||
URI string
|
||||
Legacy bool
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
}{
|
||||
URI: "192.168.0.8:502",
|
||||
}
|
||||
|
||||
if _, _, err := net.SplitHostPort(cc.URI); err != nil {
|
||||
cc.URI = "192.168.0.8:502"
|
||||
log.WARN.Printf("config: missing or invalid wallbe uri, using default %s", cc.URI)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wb := NewWallbe(cc.URI)
|
||||
|
|
@ -54,7 +52,7 @@ func NewWallbeFromConfig(log *util.Logger, other map[string]interface{}) *Wallbe
|
|||
wb.factor = 1
|
||||
}
|
||||
|
||||
return wb
|
||||
return wb, nil
|
||||
}
|
||||
|
||||
// NewWallbe creates a Wallbe charger
|
||||
|
|
@ -73,55 +71,9 @@ func NewWallbe(conn string) *Wallbe {
|
|||
factor: 10,
|
||||
}
|
||||
|
||||
// wb.showIOs()
|
||||
|
||||
return wb
|
||||
}
|
||||
|
||||
// showIOs logs all input/output register values and their configurations
|
||||
func (wb *Wallbe) showIOs() {
|
||||
// inputs
|
||||
wb.showIO("LD", 520, 200) // 200 = EN?
|
||||
wb.showIO("EN", 521, 201) // 201 = XR?
|
||||
wb.showIO("ML", 522, 202) // 202 = LD?
|
||||
wb.showIO("XR", 523, 203) // 203 = ML?
|
||||
wb.showIO("IN", 524, 208)
|
||||
|
||||
// outputs
|
||||
wb.showIO("ER", 327, 204)
|
||||
wb.showIO("LR", 328, 205)
|
||||
wb.showIO("VR", 329, 206)
|
||||
wb.showIO("CR", 330, 207)
|
||||
|
||||
if b, err := wb.client.ReadHoldingRegisters(390, 1); err != nil {
|
||||
wb.log.FATAL.Printf("%s definition %v", "Schütz", err)
|
||||
} else {
|
||||
wb.log.DEBUG.Printf("%s (%d)", "Schütz", binary.BigEndian.Uint16(b))
|
||||
}
|
||||
}
|
||||
|
||||
// showIOs logs a single input/output register's values and their configurations
|
||||
func (wb *Wallbe) showIO(input string, definition uint16, status uint16) {
|
||||
var def uint16
|
||||
var val byte
|
||||
|
||||
b, err := wb.client.ReadHoldingRegisters(definition, 1)
|
||||
if err != nil {
|
||||
wb.log.FATAL.Printf("%s definition %v", input, err)
|
||||
return
|
||||
}
|
||||
def = binary.BigEndian.Uint16(b)
|
||||
|
||||
b, err = wb.client.ReadDiscreteInputs(status, 1)
|
||||
if err != nil {
|
||||
wb.log.FATAL.Printf("%s status %v", input, err)
|
||||
return
|
||||
}
|
||||
val = b[0]
|
||||
|
||||
wb.log.DEBUG.Printf("%s = %d (%d)", input, val, def)
|
||||
}
|
||||
|
||||
// Status implements the Charger.Status interface
|
||||
func (wb *Wallbe) Status() (api.ChargeStatus, error) {
|
||||
b, err := wb.client.ReadInputRegisters(wbRegStatus, 1)
|
||||
|
|
|
|||
|
|
@ -2,18 +2,22 @@ package charger
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/andig/evcc/util"
|
||||
)
|
||||
|
||||
func TestWallbe(t *testing.T) {
|
||||
wb := NewWallbeFromConfig(util.NewLogger(""), nil)
|
||||
wb, err := NewWallbeFromConfig(nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if wb.factor != 10 {
|
||||
t.Errorf("invalid factor: %d", wb.factor)
|
||||
}
|
||||
|
||||
wb = NewWallbeFromConfig(util.NewLogger(""), map[string]interface{}{"legacy": true})
|
||||
wb, err = NewWallbeFromConfig(map[string]interface{}{"legacy": true})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if wb.factor != 1 {
|
||||
t.Errorf("invalid factor: %d", wb.factor)
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ type ConfigProvider struct {
|
|||
}
|
||||
|
||||
// Meter provides meters by name
|
||||
func (c *ConfigProvider) Meter(name string) api.Meter {
|
||||
if meter, ok := c.meters[name]; ok {
|
||||
func (cp *ConfigProvider) Meter(name string) api.Meter {
|
||||
if meter, ok := cp.meters[name]; ok {
|
||||
return meter
|
||||
}
|
||||
log.FATAL.Fatalf("config: invalid meter %s", name)
|
||||
|
|
@ -60,8 +60,8 @@ func (c *ConfigProvider) Meter(name string) api.Meter {
|
|||
}
|
||||
|
||||
// Charger provides chargers by name
|
||||
func (c *ConfigProvider) Charger(name string) api.Charger {
|
||||
if charger, ok := c.chargers[name]; ok {
|
||||
func (cp *ConfigProvider) Charger(name string) api.Charger {
|
||||
if charger, ok := cp.chargers[name]; ok {
|
||||
return charger
|
||||
}
|
||||
log.FATAL.Fatalf("config: invalid charger %s", name)
|
||||
|
|
@ -69,37 +69,49 @@ func (c *ConfigProvider) Charger(name string) api.Charger {
|
|||
}
|
||||
|
||||
// Vehicle provides vehicles by name
|
||||
func (c *ConfigProvider) Vehicle(name string) api.Vehicle {
|
||||
if vehicle, ok := c.vehicles[name]; ok {
|
||||
func (cp *ConfigProvider) Vehicle(name string) api.Vehicle {
|
||||
if vehicle, ok := cp.vehicles[name]; ok {
|
||||
return vehicle
|
||||
}
|
||||
log.FATAL.Fatalf("config: invalid vehicle %s", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConfigProvider) configure(conf config) {
|
||||
c.configureMeters(conf)
|
||||
c.configureChargers(conf)
|
||||
c.configureVehicles(conf)
|
||||
func (cp *ConfigProvider) configure(conf config) {
|
||||
cp.configureMeters(conf)
|
||||
cp.configureChargers(conf)
|
||||
cp.configureVehicles(conf)
|
||||
}
|
||||
|
||||
func (c *ConfigProvider) configureMeters(conf config) {
|
||||
c.meters = make(map[string]api.Meter)
|
||||
func (cp *ConfigProvider) configureMeters(conf config) {
|
||||
cp.meters = make(map[string]api.Meter)
|
||||
for _, cc := range conf.Meters {
|
||||
c.meters[cc.Name] = meter.NewFromConfig(log, cc.Type, cc.Other)
|
||||
m, err := meter.NewFromConfig(cc.Type, cc.Other)
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
cp.meters[cc.Name] = m
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfigProvider) configureChargers(conf config) {
|
||||
c.chargers = make(map[string]api.Charger)
|
||||
func (cp *ConfigProvider) configureChargers(conf config) {
|
||||
cp.chargers = make(map[string]api.Charger)
|
||||
for _, cc := range conf.Chargers {
|
||||
c.chargers[cc.Name] = charger.NewFromConfig(log, cc.Type, cc.Other)
|
||||
c, err := charger.NewFromConfig(cc.Type, cc.Other)
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
cp.chargers[cc.Name] = c
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfigProvider) configureVehicles(conf config) {
|
||||
c.vehicles = make(map[string]api.Vehicle)
|
||||
func (cp *ConfigProvider) configureVehicles(conf config) {
|
||||
cp.vehicles = make(map[string]api.Vehicle)
|
||||
for _, cc := range conf.Vehicles {
|
||||
c.vehicles[cc.Name] = vehicle.NewFromConfig(log, cc.Type, cc.Other)
|
||||
v, err := vehicle.NewFromConfig(cc.Type, cc.Other)
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
cp.vehicles[cc.Name] = v
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,10 @@ func configureMessengers(conf messagingConfig, cache *util.Cache) chan push.Even
|
|||
notificationHub := push.NewHub(conf.Events, cache)
|
||||
|
||||
for _, service := range conf.Services {
|
||||
impl := push.NewMessengerFromConfig(service.Type, service.Other)
|
||||
impl, err := push.NewMessengerFromConfig(service.Type, service.Other)
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
notificationHub.Add(impl)
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +90,9 @@ func configureLoadPoints(conf config, cp *ConfigProvider) (loadPoints []*core.Lo
|
|||
|
||||
// decode slice into slice of maps
|
||||
var lpc []map[string]interface{}
|
||||
util.DecodeOther(log, lps, &lpc)
|
||||
if err := util.DecodeOther(lps, &lpc); err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
for id, lpc := range lpc {
|
||||
log := util.NewLogger("lp-" + strconv.Itoa(id+1))
|
||||
|
|
|
|||
|
|
@ -89,7 +89,9 @@ type LoadPoint struct {
|
|||
// NewLoadPointFromConfig creates a new loadpoint
|
||||
func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[string]interface{}) *LoadPoint {
|
||||
lp := NewLoadPoint(log)
|
||||
util.DecodeOther(log, other, &lp)
|
||||
if err := util.DecodeOther(other, &lp); err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
// workaround mapstructure
|
||||
if lp.Mode == "0" {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,9 @@ func NewSiteFromConfig(
|
|||
loadpoints []*LoadPoint,
|
||||
) *Site {
|
||||
site := NewSite()
|
||||
util.DecodeOther(log, other, &site)
|
||||
if err := util.DecodeOther(other, &site); err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
Voltage = site.Voltage
|
||||
site.loadpoints = loadpoints
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func TestWrappedMeter(t *testing.T) {
|
|||
|
||||
mm := mock.NewMockMeter(ctrl)
|
||||
me := mock.NewMockMeterEnergy(ctrl)
|
||||
cm := &meter.MeterEnergyDecorator{Meter: mm, MeterEnergy: me}
|
||||
cm := &meter.EnergyDecorator{Meter: mm, MeterEnergy: me}
|
||||
|
||||
me.EXPECT().
|
||||
TotalEnergy().
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -3,7 +3,7 @@ module github.com/andig/evcc
|
|||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/andig/evcc-config v0.0.0-20200728142959-b798a8b6b384
|
||||
github.com/andig/evcc-config v0.0.0-20200729105416-7fbb6e0605cc
|
||||
github.com/asaskevich/EventBus v0.0.0-20200428142821-4fc0642a29f3
|
||||
github.com/avast/retry-go v2.6.0+incompatible
|
||||
github.com/benbjohnson/clock v1.0.3
|
||||
|
|
|
|||
8
go.sum
8
go.sum
|
|
@ -22,6 +22,14 @@ github.com/alvaroloes/enumer v1.1.2/go.mod h1:FxrjvuXoDAx9isTJrv4c+T410zFi0DtXIT
|
|||
github.com/andig/evcc v0.0.0-20200727161511-d58eb15f2dc9/go.mod h1:8HONEC6cC2s4k0u3QL7GIjrYOZYTOKiiXybw0FIJL0A=
|
||||
github.com/andig/evcc-config v0.0.0-20200728142959-b798a8b6b384 h1:ol6wEK2NJCIKjyVk3cyQsVKsi1QaVm77XlMNKTLidQA=
|
||||
github.com/andig/evcc-config v0.0.0-20200728142959-b798a8b6b384/go.mod h1:N0hIjIy+5E2AR1fF7Tg2IzBlblBrnFvCCaDGAaHzbWk=
|
||||
github.com/andig/evcc-config v0.0.0-20200729061908-dff8e0408168 h1:8UjWHD6HTTcTpWDD0pYlCUXaOUUe5R582EtqLy3fPig=
|
||||
github.com/andig/evcc-config v0.0.0-20200729061908-dff8e0408168/go.mod h1:N0hIjIy+5E2AR1fF7Tg2IzBlblBrnFvCCaDGAaHzbWk=
|
||||
github.com/andig/evcc-config v0.0.0-20200729092413-106691a8c13d h1:0bcJtdJmdhUfuOkEIsa7bP+eFVXXAN6W0C/AyKZhIAI=
|
||||
github.com/andig/evcc-config v0.0.0-20200729092413-106691a8c13d/go.mod h1:N0hIjIy+5E2AR1fF7Tg2IzBlblBrnFvCCaDGAaHzbWk=
|
||||
github.com/andig/evcc-config v0.0.0-20200729093325-899f5b7c2247 h1:Xv3LS07jQriFZTSZnqkvVj5QS82oy8Io1+cGF49DNl4=
|
||||
github.com/andig/evcc-config v0.0.0-20200729093325-899f5b7c2247/go.mod h1:N0hIjIy+5E2AR1fF7Tg2IzBlblBrnFvCCaDGAaHzbWk=
|
||||
github.com/andig/evcc-config v0.0.0-20200729105416-7fbb6e0605cc h1:G+Zpx2yBwqGVZPrUMm8CXpb9sOavQlH9d1CS++cV4SY=
|
||||
github.com/andig/evcc-config v0.0.0-20200729105416-7fbb6e0605cc/go.mod h1:N0hIjIy+5E2AR1fF7Tg2IzBlblBrnFvCCaDGAaHzbWk=
|
||||
github.com/andig/gosunspec v0.0.0-20200429133549-3cf6a82fed9c h1:AMtX56iHlNYVxMID7fe9efuVtaxgtdjyMeolg7q87IE=
|
||||
github.com/andig/gosunspec v0.0.0-20200429133549-3cf6a82fed9c/go.mod h1:YkshK8WMzYn1iXAZzHUO75gIqhMSan2ctgBVtBkRIyA=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
|
|
|
|||
|
|
@ -1,28 +1,26 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/andig/evcc/api"
|
||||
"github.com/andig/evcc/util"
|
||||
)
|
||||
|
||||
// NewFromConfig creates meter from configuration
|
||||
func NewFromConfig(log *util.Logger, typ string, other map[string]interface{}) api.Meter {
|
||||
var c api.Meter
|
||||
|
||||
func NewFromConfig(typ string, other map[string]interface{}) (meter api.Meter, err error) {
|
||||
switch strings.ToLower(typ) {
|
||||
case "default", "configurable":
|
||||
c = NewConfigurableFromConfig(log, other)
|
||||
meter, err = NewConfigurableFromConfig(other)
|
||||
case "modbus":
|
||||
c = NewModbusFromConfig(log, other)
|
||||
meter, err = NewModbusFromConfig(other)
|
||||
case "sma":
|
||||
c = NewSMAFromConfig(log, other)
|
||||
meter, err = NewSMAFromConfig(other)
|
||||
case "tesla", "powerwall":
|
||||
c = NewTeslaFromConfig(log, other)
|
||||
meter, err = NewTeslaFromConfig(other)
|
||||
default:
|
||||
log.FATAL.Fatalf("invalid meter type '%s'", typ)
|
||||
err = fmt.Errorf("invalid meter type: %s", typ)
|
||||
}
|
||||
|
||||
return c
|
||||
return meter, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,65 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/andig/evcc/api"
|
||||
"github.com/andig/evcc/provider"
|
||||
"github.com/andig/evcc/util"
|
||||
)
|
||||
|
||||
// MeterEnergyDecorator decorates an api.Meter with api.MeterEnergy
|
||||
type MeterEnergyDecorator struct {
|
||||
// EnergyDecorator decorates an api.Meter with api.MeterEnergy
|
||||
type EnergyDecorator struct {
|
||||
api.Meter
|
||||
api.MeterEnergy
|
||||
}
|
||||
|
||||
// NewConfigurableFromConfig creates api.Meter from config
|
||||
func NewConfigurableFromConfig(log *util.Logger, other map[string]interface{}) api.Meter {
|
||||
func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
cc := struct {
|
||||
Power provider.Config
|
||||
Energy *provider.Config // optional
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range map[string]string{"power": cc.Power.Type} {
|
||||
if v == "" {
|
||||
log.FATAL.Fatalf("default meter config: %s required", k)
|
||||
return nil, fmt.Errorf("default meter config: %s required", k)
|
||||
}
|
||||
}
|
||||
|
||||
m := NewConfigurable(provider.NewFloatGetterFromConfig(log, cc.Power))
|
||||
power, err := provider.NewFloatGetterFromConfig(cc.Power)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m, _ := NewConfigurable(power)
|
||||
|
||||
// decorate Meter with MeterEnergy
|
||||
if cc.Energy != nil {
|
||||
m = &MeterEnergyDecorator{
|
||||
energy, err := provider.NewFloatGetterFromConfig(*cc.Energy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m = &EnergyDecorator{
|
||||
Meter: m,
|
||||
MeterEnergy: NewMeterEnergy(provider.NewFloatGetterFromConfig(log, *cc.Energy)),
|
||||
MeterEnergy: NewMeterEnergy(energy),
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// NewConfigurable creates a new charger
|
||||
func NewConfigurable(currentPowerG func() (float64, error)) api.Meter {
|
||||
return &Meter{
|
||||
func NewConfigurable(currentPowerG func() (float64, error)) (api.Meter, error) {
|
||||
m := &Meter{
|
||||
currentPowerG: currentPowerG,
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Meter is an api.Meter implementation with configurable getters and setters.
|
||||
|
|
|
|||
|
|
@ -20,12 +20,15 @@ type Modbus struct {
|
|||
}
|
||||
|
||||
// NewModbusFromConfig creates api.Meter from config
|
||||
func NewModbusFromConfig(log *util.Logger, other map[string]interface{}) api.Meter {
|
||||
func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
cc := struct {
|
||||
modbus.Settings `mapstructure:",squash"`
|
||||
Power, Energy string
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// assume RTU if not set and this is a known RS485 meter model
|
||||
if cc.RTU == nil {
|
||||
|
|
@ -33,13 +36,17 @@ func NewModbusFromConfig(log *util.Logger, other map[string]interface{}) api.Met
|
|||
cc.RTU = &b
|
||||
}
|
||||
|
||||
conn := modbus.NewConnection(log, cc.URI, cc.Device, cc.Comset, cc.Baudrate, *cc.RTU)
|
||||
device, err := modbus.NewDevice(log, cc.Model, *cc.RTU)
|
||||
log := util.NewLogger("modbus")
|
||||
|
||||
log = util.NewLogger("modbus")
|
||||
conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, *cc.RTU)
|
||||
conn.Logger(log.TRACE)
|
||||
|
||||
// prepare device
|
||||
var device meters.Device
|
||||
if err == nil {
|
||||
device, err = modbus.NewDevice(cc.Model, *cc.RTU)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
conn.Slave(cc.ID)
|
||||
err = device.Initialize(conn.ModbusClient())
|
||||
|
|
@ -49,8 +56,9 @@ func NewModbusFromConfig(log *util.Logger, other map[string]interface{}) api.Met
|
|||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := &Modbus{
|
||||
|
|
@ -75,10 +83,10 @@ func NewModbusFromConfig(log *util.Logger, other map[string]interface{}) api.Met
|
|||
log.FATAL.Fatalf("invalid measurement for energy: %s", cc.Power)
|
||||
}
|
||||
|
||||
return &ModbusEnergy{m}
|
||||
return &ModbusEnergy{m}, nil
|
||||
}
|
||||
|
||||
return m
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// floatGetter executes configured modbus read operation and implements func() (float64, error)
|
||||
|
|
|
|||
16
meter/sma.go
16
meter/sma.go
|
|
@ -1,6 +1,7 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
|
@ -33,17 +34,20 @@ type SMA struct {
|
|||
}
|
||||
|
||||
// NewSMAFromConfig creates a SMA Meter from generic config
|
||||
func NewSMAFromConfig(log *util.Logger, other map[string]interface{}) api.Meter {
|
||||
func NewSMAFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
cc := struct {
|
||||
URI, Serial, Power, Energy string
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewSMA(cc.URI, cc.Serial, cc.Power, cc.Energy)
|
||||
}
|
||||
|
||||
// NewSMA creates a SMA Meter
|
||||
func NewSMA(uri, serial, power, energy string) api.Meter {
|
||||
func NewSMA(uri, serial, power, energy string) (api.Meter, error) {
|
||||
log := util.NewLogger("sma")
|
||||
|
||||
sm := &SMA{
|
||||
|
|
@ -66,17 +70,17 @@ func NewSMA(uri, serial, power, energy string) api.Meter {
|
|||
} else if serial != "" {
|
||||
sma.Instance.Subscribe(serial, sm.recv)
|
||||
} else {
|
||||
log.FATAL.Fatalf("config: missing uri or serial")
|
||||
return nil, errors.New("config: missing uri or serial")
|
||||
}
|
||||
|
||||
go sm.receive()
|
||||
|
||||
// decorate api.MeterEnergy
|
||||
if energy != "" {
|
||||
return &SMAEnergy{SMA: sm}
|
||||
return &SMAEnergy{SMA: sm}, nil
|
||||
}
|
||||
|
||||
return sm
|
||||
return sm, nil
|
||||
}
|
||||
|
||||
// update the actual meter data
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ func TestListenerProcessMessage(t *testing.T) {
|
|||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
l := &Listener{
|
||||
log: util.NewLogger("sma "),
|
||||
log: util.NewLogger("foo"),
|
||||
}
|
||||
|
||||
buffer := tc.response
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ func TestSMAUpdateMeterValues(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sm := &SMA{
|
||||
log: util.NewLogger("sma "),
|
||||
log: util.NewLogger("foo"),
|
||||
mux: util.NewWaiter(udpTimeout, func() {}),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package meter
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
|
@ -35,19 +36,22 @@ type Tesla struct {
|
|||
}
|
||||
|
||||
// NewTeslaFromConfig creates a Tesla Powerwall Meter from generic config
|
||||
func NewTeslaFromConfig(log *util.Logger, other map[string]interface{}) api.Meter {
|
||||
func NewTeslaFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
cc := struct {
|
||||
URI, Usage string
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cc.Usage == "" {
|
||||
log.FATAL.Fatalf("config: missing usage")
|
||||
return nil, errors.New("config: missing usage")
|
||||
}
|
||||
|
||||
url, err := url.ParseRequestURI(cc.URI)
|
||||
if err != nil {
|
||||
log.FATAL.Fatalf("config: invalid uri %s", cc.URI)
|
||||
return nil, fmt.Errorf("config: invalid uri %s", cc.URI)
|
||||
}
|
||||
|
||||
if url.Path == "" {
|
||||
|
|
@ -59,7 +63,7 @@ func NewTeslaFromConfig(log *util.Logger, other map[string]interface{}) api.Mete
|
|||
}
|
||||
|
||||
// NewTesla creates a Tesla Meter
|
||||
func NewTesla(uri, usage string) api.Meter {
|
||||
func NewTesla(uri, usage string) (api.Meter, error) {
|
||||
m := &Tesla{
|
||||
HTTPHelper: util.NewHTTPHelper(util.NewLogger("tesla")),
|
||||
uri: uri,
|
||||
|
|
@ -73,10 +77,10 @@ func NewTesla(uri, usage string) api.Meter {
|
|||
|
||||
// decorate api.MeterEnergy
|
||||
if m.usage == "load" || m.usage == "solar" {
|
||||
return &TeslaEnergy{Tesla: m}
|
||||
return &TeslaEnergy{Tesla: m}, nil
|
||||
}
|
||||
|
||||
return m
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// CurrentPower implements the Meter.CurrentPower interface
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ type Cached struct {
|
|||
}
|
||||
|
||||
// NewCached wraps a getter with a cache
|
||||
func NewCached(log *util.Logger, getter interface{}, cache time.Duration) *Cached {
|
||||
func NewCached(getter interface{}, cache time.Duration) *Cached {
|
||||
return &Cached{
|
||||
log: log,
|
||||
log: util.NewLogger("cache"),
|
||||
clock: clock.New(),
|
||||
getter: getter,
|
||||
cache: cache,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func TestCachedGetter(t *testing.T) {
|
|||
}
|
||||
|
||||
duration := time.Second
|
||||
c := NewCached(nil, g, duration)
|
||||
c := NewCached(g, duration)
|
||||
clck := clock.NewMock()
|
||||
c.clock = clck
|
||||
getter := c.FloatGetter()
|
||||
|
|
|
|||
|
|
@ -9,19 +9,26 @@ type calcProvider struct {
|
|||
}
|
||||
|
||||
// NewCalcFromConfig creates calc provider
|
||||
func NewCalcFromConfig(log *util.Logger, other map[string]interface{}) func() (float64, error) {
|
||||
func NewCalcFromConfig(other map[string]interface{}) (func() (float64, error), error) {
|
||||
cc := struct {
|
||||
Add []Config
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o := &calcProvider{}
|
||||
|
||||
for _, cc := range cc.Add {
|
||||
o.add = append(o.add, NewFloatGetterFromConfig(log, cc))
|
||||
f, err := NewFloatGetterFromConfig(cc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o.add = append(o.add, f)
|
||||
}
|
||||
|
||||
return o.floatGetter
|
||||
return o.floatGetter, nil
|
||||
}
|
||||
|
||||
func (o *calcProvider) floatGetter() (float64, error) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/andig/evcc/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -34,164 +36,257 @@ type scriptConfig struct {
|
|||
// MQTT singleton
|
||||
var MQTT *MqttClient
|
||||
|
||||
func mqttFromConfig(log *util.Logger, other map[string]interface{}) mqttConfig {
|
||||
func mqttFromConfig(other map[string]interface{}) (mqttConfig, error) {
|
||||
pc := mqttConfig{Scale: 1}
|
||||
if err := util.DecodeOther(other, &pc); err != nil {
|
||||
return pc, err
|
||||
}
|
||||
|
||||
if MQTT == nil {
|
||||
log.FATAL.Fatal("mqtt not configured")
|
||||
return pc, errors.New("mqtt not configured")
|
||||
}
|
||||
|
||||
var pc mqttConfig
|
||||
util.DecodeOther(log, other, &pc)
|
||||
|
||||
if pc.Scale == 0 {
|
||||
pc.Scale = 1
|
||||
}
|
||||
|
||||
return pc
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
func scriptFromConfig(log *util.Logger, other map[string]interface{}) scriptConfig {
|
||||
func scriptFromConfig(other map[string]interface{}) (scriptConfig, error) {
|
||||
var pc scriptConfig
|
||||
util.DecodeOther(log, other, &pc)
|
||||
if err := util.DecodeOther(other, &pc); err != nil {
|
||||
return pc, err
|
||||
}
|
||||
|
||||
if pc.Timeout == 0 {
|
||||
pc.Timeout = execTimeout
|
||||
}
|
||||
|
||||
return pc
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
// NewFloatGetterFromConfig creates a FloatGetter from config
|
||||
func NewFloatGetterFromConfig(log *util.Logger, config Config) (res func() (float64, error)) {
|
||||
func NewFloatGetterFromConfig(config Config) (res func() (float64, error), err error) {
|
||||
switch strings.ToLower(config.Type) {
|
||||
case "calc":
|
||||
res = NewCalcFromConfig(log, config.Other)
|
||||
res, err = NewCalcFromConfig(config.Other)
|
||||
case "http":
|
||||
res = NewHTTPProviderFromConfig(log, config.Other).FloatGetter
|
||||
var prov *HTTP
|
||||
if prov, err = NewHTTPProviderFromConfig(config.Other); err == nil {
|
||||
res = prov.FloatGetter
|
||||
}
|
||||
case "websocket", "ws":
|
||||
res = NewSocketProviderFromConfig(log, config.Other).FloatGetter
|
||||
var prov *Socket
|
||||
if prov, err = NewSocketProviderFromConfig(config.Other); err == nil {
|
||||
res = prov.FloatGetter
|
||||
}
|
||||
case "mqtt":
|
||||
pc := mqttFromConfig(log, config.Other)
|
||||
res = MQTT.FloatGetter(pc.Topic, pc.Scale, pc.Timeout)
|
||||
if pc, err := mqttFromConfig(config.Other); err == nil {
|
||||
res = MQTT.FloatGetter(pc.Topic, pc.Scale, pc.Timeout)
|
||||
}
|
||||
case "script":
|
||||
pc := scriptFromConfig(log, config.Other)
|
||||
res = NewScriptProvider(pc.Timeout).FloatGetter(pc.Cmd)
|
||||
var pc scriptConfig
|
||||
if pc, err = scriptFromConfig(config.Other); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var prov *Script
|
||||
if prov, err = NewScriptProvider(pc.Timeout); err == nil {
|
||||
res = prov.FloatGetter(pc.Cmd)
|
||||
}
|
||||
|
||||
if pc.Cache > 0 {
|
||||
res = NewCached(log, res, pc.Cache).FloatGetter()
|
||||
res = NewCached(res, pc.Cache).FloatGetter()
|
||||
}
|
||||
case "modbus":
|
||||
res = NewModbusFromConfig(log, config.Other).FloatGetter
|
||||
var prov *Modbus
|
||||
if prov, err = NewModbusFromConfig(config.Other); err == nil {
|
||||
res = prov.FloatGetter
|
||||
}
|
||||
default:
|
||||
log.FATAL.Fatal("invalid plugin type:", config.Type)
|
||||
return nil, fmt.Errorf("invalid plugin type: %s", config.Type)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewIntGetterFromConfig creates a IntGetter from config
|
||||
func NewIntGetterFromConfig(log *util.Logger, config Config) (res func() (int64, error)) {
|
||||
func NewIntGetterFromConfig(config Config) (res func() (int64, error), err error) {
|
||||
switch strings.ToLower(config.Type) {
|
||||
case "http":
|
||||
res = NewHTTPProviderFromConfig(log, config.Other).IntGetter
|
||||
var prov *HTTP
|
||||
if prov, err = NewHTTPProviderFromConfig(config.Other); err == nil {
|
||||
res = prov.IntGetter
|
||||
}
|
||||
case "websocket", "ws":
|
||||
res = NewSocketProviderFromConfig(log, config.Other).IntGetter
|
||||
var prov *Socket
|
||||
if prov, err = NewSocketProviderFromConfig(config.Other); err == nil {
|
||||
res = prov.IntGetter
|
||||
}
|
||||
case "mqtt":
|
||||
pc := mqttFromConfig(log, config.Other)
|
||||
res = MQTT.IntGetter(pc.Topic, int64(pc.Scale), pc.Timeout)
|
||||
var pc mqttConfig
|
||||
if pc, err = mqttFromConfig(config.Other); err == nil {
|
||||
res = MQTT.IntGetter(pc.Topic, int64(pc.Scale), pc.Timeout)
|
||||
}
|
||||
case "script":
|
||||
pc := scriptFromConfig(log, config.Other)
|
||||
res = NewScriptProvider(pc.Timeout).IntGetter(pc.Cmd)
|
||||
var pc scriptConfig
|
||||
if pc, err = scriptFromConfig(config.Other); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var prov *Script
|
||||
if prov, err = NewScriptProvider(pc.Timeout); err == nil {
|
||||
res = prov.IntGetter(pc.Cmd)
|
||||
}
|
||||
|
||||
if pc.Cache > 0 {
|
||||
res = NewCached(log, res, pc.Cache).IntGetter()
|
||||
res = NewCached(res, pc.Cache).IntGetter()
|
||||
}
|
||||
case "modbus":
|
||||
res = NewModbusFromConfig(log, config.Other).IntGetter
|
||||
var prov *Modbus
|
||||
if prov, err = NewModbusFromConfig(config.Other); err == nil {
|
||||
res = prov.IntGetter
|
||||
}
|
||||
default:
|
||||
log.FATAL.Fatal("invalid plugin type:", config.Type)
|
||||
err = fmt.Errorf("invalid plugin type: %s", config.Type)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewStringGetterFromConfig creates a StringGetter from config
|
||||
func NewStringGetterFromConfig(log *util.Logger, config Config) (res func() (string, error)) {
|
||||
func NewStringGetterFromConfig(config Config) (res func() (string, error), err error) {
|
||||
switch strings.ToLower(config.Type) {
|
||||
case "http":
|
||||
res = NewHTTPProviderFromConfig(log, config.Other).StringGetter
|
||||
var prov *HTTP
|
||||
if prov, err = NewHTTPProviderFromConfig(config.Other); err == nil {
|
||||
res = prov.StringGetter
|
||||
}
|
||||
case "websocket", "ws":
|
||||
res = NewSocketProviderFromConfig(log, config.Other).StringGetter
|
||||
var prov *Socket
|
||||
if prov, err = NewSocketProviderFromConfig(config.Other); err == nil {
|
||||
res = prov.StringGetter
|
||||
}
|
||||
case "mqtt":
|
||||
pc := mqttFromConfig(log, config.Other)
|
||||
res = MQTT.StringGetter(pc.Topic, pc.Timeout)
|
||||
var pc mqttConfig
|
||||
if pc, err = mqttFromConfig(config.Other); err == nil {
|
||||
res = MQTT.StringGetter(pc.Topic, pc.Timeout)
|
||||
}
|
||||
case "script":
|
||||
pc := scriptFromConfig(log, config.Other)
|
||||
res = NewScriptProvider(pc.Timeout).StringGetter(pc.Cmd)
|
||||
var pc scriptConfig
|
||||
if pc, err = scriptFromConfig(config.Other); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var prov *Script
|
||||
if prov, err = NewScriptProvider(pc.Timeout); err == nil {
|
||||
res = prov.StringGetter(pc.Cmd)
|
||||
}
|
||||
|
||||
if pc.Cache > 0 {
|
||||
res = NewCached(log, res, pc.Cache).StringGetter()
|
||||
res = NewCached(res, pc.Cache).StringGetter()
|
||||
}
|
||||
case "combined", "openwb":
|
||||
res = openWBStatusFromConfig(log, config.Other)
|
||||
res, err = openWBStatusFromConfig(config.Other)
|
||||
default:
|
||||
log.FATAL.Fatal("invalid plugin type:", config.Type)
|
||||
err = fmt.Errorf("invalid plugin type: %s", config.Type)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewBoolGetterFromConfig creates a BoolGetter from config
|
||||
func NewBoolGetterFromConfig(log *util.Logger, config Config) (res func() (bool, error)) {
|
||||
func NewBoolGetterFromConfig(config Config) (res func() (bool, error), err error) {
|
||||
switch strings.ToLower(config.Type) {
|
||||
case "http":
|
||||
res = NewHTTPProviderFromConfig(log, config.Other).BoolGetter
|
||||
var prov *HTTP
|
||||
if prov, err = NewHTTPProviderFromConfig(config.Other); err == nil {
|
||||
res = prov.BoolGetter
|
||||
}
|
||||
case "websocket", "ws":
|
||||
res = NewSocketProviderFromConfig(log, config.Other).BoolGetter
|
||||
var prov *Socket
|
||||
if prov, err = NewSocketProviderFromConfig(config.Other); err == nil {
|
||||
res = prov.BoolGetter
|
||||
}
|
||||
case "mqtt":
|
||||
pc := mqttFromConfig(log, config.Other)
|
||||
res = MQTT.BoolGetter(pc.Topic, pc.Timeout)
|
||||
var pc mqttConfig
|
||||
if pc, err = mqttFromConfig(config.Other); err == nil {
|
||||
res = MQTT.BoolGetter(pc.Topic, pc.Timeout)
|
||||
}
|
||||
case "script":
|
||||
pc := scriptFromConfig(log, config.Other)
|
||||
res = NewScriptProvider(pc.Timeout).BoolGetter(pc.Cmd)
|
||||
var pc scriptConfig
|
||||
if pc, err = scriptFromConfig(config.Other); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var prov *Script
|
||||
if prov, err = NewScriptProvider(pc.Timeout); err == nil {
|
||||
res = prov.BoolGetter(pc.Cmd)
|
||||
}
|
||||
|
||||
if pc.Cache > 0 {
|
||||
res = NewCached(log, res, pc.Cache).BoolGetter()
|
||||
res = NewCached(res, pc.Cache).BoolGetter()
|
||||
}
|
||||
default:
|
||||
log.FATAL.Fatal("invalid plugin type:", config.Type)
|
||||
err = fmt.Errorf("invalid plugin type: %s", config.Type)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewIntSetterFromConfig creates a IntSetter from config
|
||||
func NewIntSetterFromConfig(log *util.Logger, param string, config Config) (res func(int64) error) {
|
||||
func NewIntSetterFromConfig(param string, config Config) (res func(int64) error, err error) {
|
||||
switch strings.ToLower(config.Type) {
|
||||
case "http":
|
||||
res = NewHTTPProviderFromConfig(log, config.Other).IntSetter
|
||||
var prov *HTTP
|
||||
if prov, err = NewHTTPProviderFromConfig(config.Other); err == nil {
|
||||
res = prov.IntSetter
|
||||
}
|
||||
case "mqtt":
|
||||
pc := mqttFromConfig(log, config.Other)
|
||||
res = MQTT.IntSetter(param, pc.Topic, pc.Payload)
|
||||
var pc mqttConfig
|
||||
if pc, err = mqttFromConfig(config.Other); err == nil {
|
||||
res = MQTT.IntSetter(param, pc.Topic, pc.Payload)
|
||||
}
|
||||
case "script":
|
||||
pc := scriptFromConfig(log, config.Other)
|
||||
script := NewScriptProvider(pc.Timeout)
|
||||
res = script.IntSetter(param, pc.Cmd)
|
||||
var pc scriptConfig
|
||||
if pc, err = scriptFromConfig(config.Other); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var prov *Script
|
||||
if prov, err = NewScriptProvider(pc.Timeout); err == nil {
|
||||
res = prov.IntSetter(param, pc.Cmd)
|
||||
}
|
||||
default:
|
||||
log.FATAL.Fatalf("invalid setter type %s", config.Type)
|
||||
err = fmt.Errorf("invalid setter type %s", config.Type)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewBoolSetterFromConfig creates a BoolSetter from config
|
||||
func NewBoolSetterFromConfig(log *util.Logger, param string, config Config) (res func(bool) error) {
|
||||
func NewBoolSetterFromConfig(param string, config Config) (res func(bool) error, err error) {
|
||||
switch strings.ToLower(config.Type) {
|
||||
case "http":
|
||||
res = NewHTTPProviderFromConfig(log, config.Other).BoolSetter
|
||||
var prov *HTTP
|
||||
if prov, err = NewHTTPProviderFromConfig(config.Other); err == nil {
|
||||
res = prov.BoolSetter
|
||||
}
|
||||
case "mqtt":
|
||||
pc := mqttFromConfig(log, config.Other)
|
||||
res = MQTT.BoolSetter(param, pc.Topic, pc.Payload)
|
||||
var pc mqttConfig
|
||||
if pc, err = mqttFromConfig(config.Other); err == nil {
|
||||
res = MQTT.BoolSetter(param, pc.Topic, pc.Payload)
|
||||
}
|
||||
case "script":
|
||||
pc := scriptFromConfig(log, config.Other)
|
||||
script := NewScriptProvider(pc.Timeout)
|
||||
res = script.BoolSetter(param, pc.Cmd)
|
||||
var pc scriptConfig
|
||||
if pc, err = scriptFromConfig(config.Other); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var prov *Script
|
||||
if prov, err = NewScriptProvider(pc.Timeout); err == nil {
|
||||
res = prov.BoolSetter(param, pc.Cmd)
|
||||
}
|
||||
default:
|
||||
log.FATAL.Fatalf("invalid setter type %s", config.Type)
|
||||
err = fmt.Errorf("invalid setter type %s", config.Type)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ func NewAuth(log *util.Logger, auth Auth, headers map[string]string) {
|
|||
}
|
||||
|
||||
// NewHTTPProviderFromConfig creates a HTTP provider
|
||||
func NewHTTPProviderFromConfig(log *util.Logger, other map[string]interface{}) *HTTP {
|
||||
func NewHTTPProviderFromConfig(other map[string]interface{}) (*HTTP, error) {
|
||||
cc := struct {
|
||||
URI, Method string
|
||||
Headers map[string]string
|
||||
|
|
@ -51,12 +51,15 @@ func NewHTTPProviderFromConfig(log *util.Logger, other map[string]interface{}) *
|
|||
Insecure bool
|
||||
Auth Auth
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
logger := util.NewLogger("http")
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log := util.NewLogger("http")
|
||||
|
||||
p := &HTTP{
|
||||
HTTPHelper: util.NewHTTPHelper(logger),
|
||||
HTTPHelper: util.NewHTTPHelper(log),
|
||||
url: cc.URI,
|
||||
method: cc.Method,
|
||||
headers: cc.Headers,
|
||||
|
|
@ -82,13 +85,13 @@ func NewHTTPProviderFromConfig(log *util.Logger, other map[string]interface{}) *
|
|||
if cc.Jq != "" {
|
||||
op, err := gojq.Parse(cc.Jq)
|
||||
if err != nil {
|
||||
log.FATAL.Fatalf("config: invalid jq query: %s", p.jq)
|
||||
return nil, fmt.Errorf("config: invalid jq query: %s", p.jq)
|
||||
}
|
||||
|
||||
p.jq = op
|
||||
}
|
||||
|
||||
return p
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// request executed the configured request
|
||||
|
|
|
|||
|
|
@ -23,14 +23,19 @@ type Modbus struct {
|
|||
}
|
||||
|
||||
// NewModbusFromConfig creates Modbus plugin
|
||||
func NewModbusFromConfig(log *util.Logger, other map[string]interface{}) *Modbus {
|
||||
func NewModbusFromConfig(other map[string]interface{}) (*Modbus, error) {
|
||||
cc := struct {
|
||||
modbus.Settings `mapstructure:",squash"`
|
||||
Register modbus.Register
|
||||
Value string
|
||||
Scale float64
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
}{
|
||||
Scale: 1,
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// assume RTU if not set and this is a known RS485 meter model
|
||||
if cc.RTU == nil {
|
||||
|
|
@ -38,16 +43,19 @@ func NewModbusFromConfig(log *util.Logger, other map[string]interface{}) *Modbus
|
|||
cc.RTU = &b
|
||||
}
|
||||
|
||||
log = util.NewLogger("modb")
|
||||
conn := modbus.NewConnection(log, cc.URI, cc.Device, cc.Comset, cc.Baudrate, *cc.RTU)
|
||||
conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, *cc.RTU)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log := util.NewLogger("modbus")
|
||||
conn.Logger(log.TRACE)
|
||||
|
||||
var err error
|
||||
var device meters.Device
|
||||
var op modbus.Operation
|
||||
|
||||
if cc.Value != "" && cc.Register.Decode != "" {
|
||||
log.FATAL.Fatalf("config: modbus cannot have value and register both")
|
||||
return nil, errors.New("config: modbus cannot have value and register both")
|
||||
}
|
||||
|
||||
if cc.Value == "" && cc.Register.Decode == "" {
|
||||
|
|
@ -58,7 +66,7 @@ func NewModbusFromConfig(log *util.Logger, other map[string]interface{}) *Modbus
|
|||
// model + value configured
|
||||
if cc.Value != "" {
|
||||
if err := modbus.ParseOperation(device, cc.Value, &op); err != nil {
|
||||
log.FATAL.Fatalf("config: invalid value %s", cc.Value)
|
||||
return nil, fmt.Errorf("config: invalid value %s", cc.Value)
|
||||
}
|
||||
|
||||
// if sunspec reading configured make sure model is defined or device won't be initalized
|
||||
|
|
@ -76,7 +84,7 @@ func NewModbusFromConfig(log *util.Logger, other map[string]interface{}) *Modbus
|
|||
|
||||
// model configured
|
||||
if cc.Model != "" {
|
||||
device, err = modbus.NewDevice(log, cc.Model, *cc.RTU)
|
||||
device, err = modbus.NewDevice(cc.Model, *cc.RTU)
|
||||
|
||||
// prepare device
|
||||
if err == nil {
|
||||
|
|
@ -89,15 +97,12 @@ func NewModbusFromConfig(log *util.Logger, other map[string]interface{}) *Modbus
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cc.Scale == 0 {
|
||||
cc.Scale = 1
|
||||
}
|
||||
|
||||
return &Modbus{
|
||||
mb := &Modbus{
|
||||
log: log,
|
||||
conn: conn,
|
||||
device: device,
|
||||
|
|
@ -105,6 +110,7 @@ func NewModbusFromConfig(log *util.Logger, other map[string]interface{}) *Modbus
|
|||
scale: cc.Scale,
|
||||
slaveID: cc.ID,
|
||||
}
|
||||
return mb, nil
|
||||
}
|
||||
|
||||
// FloatGetter executes configured modbus read operation and implements func() (float64, error)
|
||||
|
|
|
|||
|
|
@ -9,18 +9,31 @@ type openWBStatusProvider struct {
|
|||
plugged, charging func() (bool, error)
|
||||
}
|
||||
|
||||
func openWBStatusFromConfig(log *util.Logger, other map[string]interface{}) func() (string, error) {
|
||||
func openWBStatusFromConfig(other map[string]interface{}) (func() (string, error), error) {
|
||||
cc := struct {
|
||||
Plugged, Charging Config
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
o := &openWBStatusProvider{
|
||||
plugged: NewBoolGetterFromConfig(log, cc.Plugged),
|
||||
charging: NewBoolGetterFromConfig(log, cc.Charging),
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return o.stringGetter
|
||||
plugged, err := NewBoolGetterFromConfig(cc.Plugged)
|
||||
|
||||
var charging func() (bool, error)
|
||||
if err == nil {
|
||||
charging, err = NewBoolGetterFromConfig(cc.Charging)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o := &openWBStatusProvider{
|
||||
plugged: plugged,
|
||||
charging: charging,
|
||||
}
|
||||
|
||||
return o.stringGetter, nil
|
||||
}
|
||||
|
||||
func (o *openWBStatusProvider) stringGetter() (string, error) {
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@ type Script struct {
|
|||
|
||||
// NewScriptProvider creates a script provider.
|
||||
// Script execution is aborted after given timeout.
|
||||
func NewScriptProvider(timeout time.Duration) *Script {
|
||||
return &Script{
|
||||
func NewScriptProvider(timeout time.Duration) (*Script, error) {
|
||||
s := &Script{
|
||||
log: util.NewLogger("exec"),
|
||||
timeout: timeout,
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// StringGetter returns string from exec result. Only STDOUT is considered.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import (
|
|||
"github.com/itchyny/gojq"
|
||||
)
|
||||
|
||||
const retryDelay = 5 * time.Second
|
||||
|
||||
// Socket implements websocket request provider
|
||||
type Socket struct {
|
||||
*util.HTTPHelper
|
||||
|
|
@ -26,7 +28,7 @@ type Socket struct {
|
|||
}
|
||||
|
||||
// NewSocketProviderFromConfig creates a HTTP provider
|
||||
func NewSocketProviderFromConfig(log *util.Logger, other map[string]interface{}) *Socket {
|
||||
func NewSocketProviderFromConfig(other map[string]interface{}) (*Socket, error) {
|
||||
cc := struct {
|
||||
URI string
|
||||
Headers map[string]string
|
||||
|
|
@ -36,13 +38,15 @@ func NewSocketProviderFromConfig(log *util.Logger, other map[string]interface{})
|
|||
Auth Auth
|
||||
Timeout time.Duration
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger := util.NewLogger("ws")
|
||||
log := util.NewLogger("ws")
|
||||
|
||||
p := &Socket{
|
||||
HTTPHelper: util.NewHTTPHelper(logger),
|
||||
mux: util.NewWaiter(cc.Timeout, func() { logger.TRACE.Println("wait for initial value") }),
|
||||
HTTPHelper: util.NewHTTPHelper(log),
|
||||
mux: util.NewWaiter(cc.Timeout, func() { log.TRACE.Println("wait for initial value") }),
|
||||
url: cc.URI,
|
||||
headers: cc.Headers,
|
||||
scale: cc.Scale,
|
||||
|
|
@ -66,7 +70,7 @@ func NewSocketProviderFromConfig(log *util.Logger, other map[string]interface{})
|
|||
if cc.Jq != "" {
|
||||
op, err := gojq.Parse(cc.Jq)
|
||||
if err != nil {
|
||||
log.FATAL.Fatalf("config: invalid jq query: %s", p.jq)
|
||||
return nil, fmt.Errorf("config: invalid jq query: %s", p.jq)
|
||||
}
|
||||
|
||||
p.jq = op
|
||||
|
|
@ -74,7 +78,7 @@ func NewSocketProviderFromConfig(log *util.Logger, other map[string]interface{})
|
|||
|
||||
go p.listen()
|
||||
|
||||
return p
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *Socket) listen() {
|
||||
|
|
@ -88,7 +92,9 @@ func (p *Socket) listen() {
|
|||
for {
|
||||
client, _, err := websocket.DefaultDialer.Dial(p.url, headers)
|
||||
if err != nil {
|
||||
log.ERROR.Println("dial:", err)
|
||||
log.ERROR.Println(err)
|
||||
time.Sleep(retryDelay)
|
||||
continue
|
||||
}
|
||||
|
||||
for {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package push
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/andig/evcc/util"
|
||||
|
|
@ -19,18 +20,21 @@ type EventTemplate struct {
|
|||
var log = util.NewLogger("push")
|
||||
|
||||
// NewMessengerFromConfig creates a new messenger
|
||||
func NewMessengerFromConfig(typ string, other map[string]interface{}) Sender {
|
||||
func NewMessengerFromConfig(typ string, other map[string]interface{}) (res Sender, err error) {
|
||||
switch strings.ToLower(typ) {
|
||||
case "pushover":
|
||||
var cc pushOverConfig
|
||||
util.DecodeOther(log, other, &cc)
|
||||
return NewPushOverMessenger(cc.App, cc.Recipients)
|
||||
if err = util.DecodeOther(other, &cc); err == nil {
|
||||
res = NewPushOverMessenger(cc.App, cc.Recipients)
|
||||
}
|
||||
case "telegram":
|
||||
var cc telegramConfig
|
||||
util.DecodeOther(log, other, &cc)
|
||||
return NewTelegramMessenger(cc.Token, cc.Chats)
|
||||
if err = util.DecodeOther(other, &cc); err == nil {
|
||||
res = NewTelegramMessenger(cc.Token, cc.Chats)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("unknown messenger type: %s", typ)
|
||||
}
|
||||
|
||||
log.FATAL.Fatalf("unknown messenger type: %s", typ)
|
||||
return nil
|
||||
return res, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
)
|
||||
|
||||
// DecodeOther uses mapstructure to decode into target structure. Unused keys cause errors.
|
||||
func DecodeOther(log *Logger, other interface{}, cc interface{}) {
|
||||
func DecodeOther(other interface{}, cc interface{}) error {
|
||||
decoderConfig := &mapstructure.DecoderConfig{
|
||||
Result: cc,
|
||||
ErrorUnused: true,
|
||||
|
|
@ -14,11 +14,9 @@ func DecodeOther(log *Logger, other interface{}, cc interface{}) {
|
|||
}
|
||||
|
||||
decoder, err := mapstructure.NewDecoder(decoderConfig)
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
if err == nil {
|
||||
err = decoder.Decode(other)
|
||||
}
|
||||
|
||||
if err := decoder.Decode(other); err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package modbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/andig/evcc/util"
|
||||
"github.com/volkszaehler/mbmd/meters"
|
||||
"github.com/volkszaehler/mbmd/meters/rs485"
|
||||
"github.com/volkszaehler/mbmd/meters/sunspec"
|
||||
|
|
@ -41,7 +41,7 @@ func registeredConnection(key string, newConn meters.Connection) meters.Connecti
|
|||
}
|
||||
|
||||
// NewConnection creates physical modbus device from config
|
||||
func NewConnection(log *util.Logger, uri, device, comset string, baudrate int, rtu bool) (conn meters.Connection) {
|
||||
func NewConnection(uri, device, comset string, baudrate int, rtu bool) (conn meters.Connection, err error) {
|
||||
if device != "" {
|
||||
conn = registeredConnection(device, meters.NewRTU(device, baudrate, comset))
|
||||
}
|
||||
|
|
@ -55,14 +55,14 @@ func NewConnection(log *util.Logger, uri, device, comset string, baudrate int, r
|
|||
}
|
||||
|
||||
if conn == nil {
|
||||
log.FATAL.Fatalf("config: invalid modbus configuration: need either uri or device")
|
||||
return nil, errors.New("config: invalid modbus configuration: need either uri or device")
|
||||
}
|
||||
|
||||
return conn
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// NewDevice creates physical modbus device from config
|
||||
func NewDevice(log *util.Logger, model string, isRS485 bool) (device meters.Device, err error) {
|
||||
func NewDevice(model string, isRS485 bool) (device meters.Device, err error) {
|
||||
if isRS485 {
|
||||
device, err = rs485.NewDevice(strings.ToUpper(model))
|
||||
} else {
|
||||
|
|
@ -70,7 +70,7 @@ func NewDevice(log *util.Logger, model string, isRS485 bool) (device meters.Devi
|
|||
}
|
||||
|
||||
if device == nil {
|
||||
log.FATAL.Fatalf("config: invalid modbus configuration: need either uri or device")
|
||||
err = errors.New("config: invalid modbus configuration: need either uri or device")
|
||||
}
|
||||
|
||||
return device, err
|
||||
|
|
|
|||
|
|
@ -54,14 +54,16 @@ type Audi struct {
|
|||
}
|
||||
|
||||
// NewAudiFromConfig creates a new vehicle
|
||||
func NewAudiFromConfig(log *util.Logger, other map[string]interface{}) api.Vehicle {
|
||||
func NewAudiFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
||||
cc := struct {
|
||||
Title string
|
||||
Capacity int64
|
||||
User, Password, VIN string
|
||||
Cache time.Duration
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &Audi{
|
||||
embed: &embed{cc.Title, cc.Capacity},
|
||||
|
|
@ -71,9 +73,9 @@ func NewAudiFromConfig(log *util.Logger, other map[string]interface{}) api.Vehic
|
|||
vin: cc.VIN,
|
||||
}
|
||||
|
||||
v.chargeStateG = provider.NewCached(log, v.chargeState, cc.Cache).FloatGetter()
|
||||
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).FloatGetter()
|
||||
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (v *Audi) apiURL(service, part string) string {
|
||||
|
|
|
|||
|
|
@ -40,16 +40,19 @@ type BMW struct {
|
|||
}
|
||||
|
||||
// NewBMWFromConfig creates a new vehicle
|
||||
func NewBMWFromConfig(log *util.Logger, other map[string]interface{}) api.Vehicle {
|
||||
func NewBMWFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
||||
cc := struct {
|
||||
Title string
|
||||
Capacity int64
|
||||
User, Password, VIN string
|
||||
Cache time.Duration
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
log = util.NewLogger("bmw ")
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log := util.NewLogger("bmw")
|
||||
|
||||
v := &BMW{
|
||||
embed: &embed{cc.Title, cc.Capacity},
|
||||
|
|
@ -62,20 +65,20 @@ func NewBMWFromConfig(log *util.Logger, other map[string]interface{}) api.Vehicl
|
|||
if cc.VIN == "" {
|
||||
vehicles, err := v.vehicles()
|
||||
if err != nil {
|
||||
log.FATAL.Fatalf("cannot get vehicles: %v", err)
|
||||
return nil, fmt.Errorf("cannot get vehicles: %v", err)
|
||||
}
|
||||
|
||||
if len(vehicles) != 1 {
|
||||
log.FATAL.Fatalf("cannot find vehicle: %v", vehicles)
|
||||
return nil, fmt.Errorf("cannot find vehicle: %v", vehicles)
|
||||
}
|
||||
|
||||
v.vin = vehicles[0].Vin
|
||||
log.DEBUG.Printf("found vehicle: %v", v.vin)
|
||||
}
|
||||
|
||||
v.chargeStateG = provider.NewCached(log, v.chargeState, cc.Cache).FloatGetter()
|
||||
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).FloatGetter()
|
||||
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (v *BMW) login(user, password string) error {
|
||||
|
|
|
|||
|
|
@ -1,34 +1,32 @@
|
|||
package vehicle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/andig/evcc/api"
|
||||
"github.com/andig/evcc/util"
|
||||
)
|
||||
|
||||
// NewFromConfig creates vehicle from configuration
|
||||
func NewFromConfig(log *util.Logger, typ string, other map[string]interface{}) api.Vehicle {
|
||||
var c api.Vehicle
|
||||
|
||||
func NewFromConfig(typ string, other map[string]interface{}) (v api.Vehicle, err error) {
|
||||
switch strings.ToLower(typ) {
|
||||
case "default", "configurable":
|
||||
c = NewConfigurableFromConfig(log, other)
|
||||
v, err = NewConfigurableFromConfig(other)
|
||||
case "audi", "etron":
|
||||
c = NewAudiFromConfig(log, other)
|
||||
v, err = NewAudiFromConfig(other)
|
||||
case "bmw", "i3":
|
||||
c = NewBMWFromConfig(log, other)
|
||||
v, err = NewBMWFromConfig(other)
|
||||
case "tesla", "model3", "model 3", "models", "model s":
|
||||
c = NewTeslaFromConfig(log, other)
|
||||
v, err = NewTeslaFromConfig(other)
|
||||
case "nissan", "leaf":
|
||||
c = NewNissanFromConfig(log, other)
|
||||
v, err = NewNissanFromConfig(other)
|
||||
case "renault", "zoe":
|
||||
c = NewRenaultFromConfig(log, other)
|
||||
v, err = NewRenaultFromConfig(other)
|
||||
case "porsche", "taycan":
|
||||
c = NewPorscheFromConfig(log, other)
|
||||
v, err = NewPorscheFromConfig(other)
|
||||
default:
|
||||
log.FATAL.Fatalf("invalid vehicle type '%s'", typ)
|
||||
err = fmt.Errorf("invalid vehicle type: %s", typ)
|
||||
}
|
||||
|
||||
return c
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,17 +18,18 @@ type Nissan struct {
|
|||
}
|
||||
|
||||
// NewNissanFromConfig creates a new vehicle
|
||||
func NewNissanFromConfig(log *util.Logger, other map[string]interface{}) api.Vehicle {
|
||||
func NewNissanFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
||||
cc := struct {
|
||||
Title string
|
||||
Capacity int64
|
||||
User, Password, Region string
|
||||
Cache time.Duration
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
}{
|
||||
Region: carwings.RegionEurope,
|
||||
}
|
||||
|
||||
if cc.Region == "" {
|
||||
cc.Region = carwings.RegionEurope
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session := &carwings.Session{
|
||||
|
|
@ -36,7 +37,7 @@ func NewNissanFromConfig(log *util.Logger, other map[string]interface{}) api.Veh
|
|||
}
|
||||
|
||||
if err := session.Connect(cc.User, cc.Password); err != nil {
|
||||
log.FATAL.Fatalf("cannot create nissan: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &Nissan{
|
||||
|
|
@ -44,9 +45,9 @@ func NewNissanFromConfig(log *util.Logger, other map[string]interface{}) api.Veh
|
|||
session: session,
|
||||
}
|
||||
|
||||
v.chargeStateG = provider.NewCached(log, v.chargeState, cc.Cache).FloatGetter()
|
||||
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).FloatGetter()
|
||||
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// chargeState implements the Vehicle.ChargeState interface
|
||||
|
|
|
|||
|
|
@ -59,14 +59,16 @@ type Porsche struct {
|
|||
}
|
||||
|
||||
// NewPorscheFromConfig creates a new vehicle
|
||||
func NewPorscheFromConfig(log *util.Logger, other map[string]interface{}) api.Vehicle {
|
||||
func NewPorscheFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
||||
cc := struct {
|
||||
Title string
|
||||
Capacity int64
|
||||
User, Password, VIN string
|
||||
Cache time.Duration
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &Porsche{
|
||||
embed: &embed{cc.Title, cc.Capacity},
|
||||
|
|
@ -76,9 +78,9 @@ func NewPorscheFromConfig(log *util.Logger, other map[string]interface{}) api.Ve
|
|||
vin: cc.VIN,
|
||||
}
|
||||
|
||||
v.chargeStateG = provider.NewCached(log, v.chargeState, cc.Cache).FloatGetter()
|
||||
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).FloatGetter()
|
||||
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// login with a my Porsche account
|
||||
|
|
|
|||
|
|
@ -94,24 +94,25 @@ type Renault struct {
|
|||
}
|
||||
|
||||
// NewRenaultFromConfig creates a new vehicle
|
||||
func NewRenaultFromConfig(log *util.Logger, other map[string]interface{}) api.Vehicle {
|
||||
func NewRenaultFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
||||
cc := struct {
|
||||
Title string
|
||||
Capacity int64
|
||||
User, Password, Region, VIN string
|
||||
Cache time.Duration
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
if cc.Region == "" {
|
||||
cc.Region = "de_DE"
|
||||
}{
|
||||
Region: "de_DE",
|
||||
}
|
||||
|
||||
logger := util.NewLogger("zoe")
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log := util.NewLogger("renault")
|
||||
|
||||
v := &Renault{
|
||||
embed: &embed{cc.Title, cc.Capacity},
|
||||
HTTPHelper: util.NewHTTPHelper(logger),
|
||||
HTTPHelper: util.NewHTTPHelper(log),
|
||||
user: cc.User,
|
||||
password: cc.Password,
|
||||
vin: cc.VIN,
|
||||
|
|
@ -122,12 +123,12 @@ func NewRenaultFromConfig(log *util.Logger, other map[string]interface{}) api.Ve
|
|||
err = v.authFlow()
|
||||
}
|
||||
if err != nil {
|
||||
v.HTTPHelper.Log.FATAL.Fatalf("cannot create renault: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v.chargeStateG = provider.NewCached(logger, v.chargeState, cc.Cache).FloatGetter()
|
||||
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).FloatGetter()
|
||||
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (v *Renault) apiKeys(region string) error {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package vehicle
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/andig/evcc/api"
|
||||
|
|
@ -18,7 +19,7 @@ type Tesla struct {
|
|||
}
|
||||
|
||||
// NewTeslaFromConfig creates a new Tesla vehicle
|
||||
func NewTeslaFromConfig(log *util.Logger, other map[string]interface{}) api.Vehicle {
|
||||
func NewTeslaFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
||||
cc := struct {
|
||||
Title string
|
||||
Capacity int64
|
||||
|
|
@ -27,7 +28,10 @@ func NewTeslaFromConfig(log *util.Logger, other map[string]interface{}) api.Vehi
|
|||
VIN string
|
||||
Cache time.Duration
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := tesla.NewClient(&tesla.Auth{
|
||||
ClientID: cc.ClientID,
|
||||
|
|
@ -36,12 +40,12 @@ func NewTeslaFromConfig(log *util.Logger, other map[string]interface{}) api.Vehi
|
|||
Password: cc.Password,
|
||||
})
|
||||
if err != nil {
|
||||
log.FATAL.Fatalf("cannot create tesla: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vehicles, err := client.Vehicles()
|
||||
if err != nil {
|
||||
log.FATAL.Fatalf("cannot create tesla: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &Tesla{
|
||||
|
|
@ -59,13 +63,13 @@ func NewTeslaFromConfig(log *util.Logger, other map[string]interface{}) api.Vehi
|
|||
}
|
||||
|
||||
if v.vehicle == nil {
|
||||
log.FATAL.Fatal("cannot create tesla: vin not found")
|
||||
return nil, errors.New("vin not found")
|
||||
}
|
||||
|
||||
v.chargeStateG = provider.NewCached(log, v.chargeState, cc.Cache).FloatGetter()
|
||||
v.chargedEnergyG = provider.NewCached(log, v.chargedEnergy, cc.Cache).FloatGetter()
|
||||
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).FloatGetter()
|
||||
v.chargedEnergyG = provider.NewCached(v.chargedEnergy, cc.Cache).FloatGetter()
|
||||
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// chargeState implements the Vehicle.ChargeState interface
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package vehicle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/andig/evcc/api"
|
||||
|
|
@ -30,30 +31,39 @@ type Vehicle struct {
|
|||
}
|
||||
|
||||
// NewConfigurableFromConfig creates a new Vehicle
|
||||
func NewConfigurableFromConfig(log *util.Logger, other map[string]interface{}) api.Vehicle {
|
||||
func NewConfigurableFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
||||
cc := struct {
|
||||
Title string
|
||||
Capacity int64
|
||||
Charge provider.Config
|
||||
Cache time.Duration
|
||||
}{}
|
||||
util.DecodeOther(log, other, &cc)
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range map[string]string{"charge": cc.Charge.Type} {
|
||||
if v == "" {
|
||||
log.FATAL.Fatalf("default vehicle config: %s required", k)
|
||||
return nil, fmt.Errorf("default vehicle config: %s required", k)
|
||||
}
|
||||
}
|
||||
|
||||
getter := provider.NewFloatGetterFromConfig(log, cc.Charge)
|
||||
if cc.Cache > 0 {
|
||||
getter = provider.NewCached(log, getter, cc.Cache).FloatGetter()
|
||||
getter, err := provider.NewFloatGetterFromConfig(cc.Charge)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Vehicle{
|
||||
if cc.Cache > 0 {
|
||||
getter = provider.NewCached(getter, cc.Cache).FloatGetter()
|
||||
}
|
||||
|
||||
v := &Vehicle{
|
||||
embed: &embed{cc.Title, cc.Capacity},
|
||||
chargeG: getter,
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ChargeState implements the Vehicle.ChargeState interface
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue