Repository: evcc-io/evcc
Pull request #7735 (2023-04-29 13:22:59)

Homematic: Reduce API requests to relax CCU duty cycle
Enhancement of CCU access logic:

- Switch reading single device channel values (getValue) to multiple values (getParamset)
- Introduction of caching to avoid multiple CCU accesses, causing CCU duty cycle problems
- Code simplification and refactoring

Refer to #7430

thierolm: @Marty56 wäre prima, wenn du die nightly Version bzgl. DutyCycle testen könntest, wenn @andig den PR gemerged hat. 
An meiner Test CCU hängen nur zwei Geräte, die hatte bisher nie Probleme ...
andig: Magst du noch Cache Parameter mit Default 1s einbauen und `provider.Cacheable` nutzen?
thierolm: Hallo Andi, ich habe `provider.Cacheable` eingebaut (habe mir die daheimladen.go als Beispiel angeschaut).
Kannst du bitte noch mal schauen, an welcher Stelle ich den Chache Reset einbauen muss/soll? Oder brauche ich den gar nicht?
thierolm: Ich denke wir brauchen einen Cache reset zumindest in `Enable`, um korrekte Status Informationen zu bekommen.
andig: Ich hab ein wenig umgebaut- schaust Du nochmal drauf?
thierolm: Läuft alles prima! :-)
```bash
thierolm@LAPTOP-KQMMVN3H:~/git-repos/evcc$ ./evcc -c ./evcc_homematictest.yaml charger -l trace -d
[main  ] INFO 2023/04/30 01:08:53 evcc 0.116.5 (09c9154c)
[main  ] INFO 2023/04/30 01:08:53 using config file: ./evcc_homematictest.yaml
[db    ] INFO 2023/04/30 01:08:53 using sqlite database: /home/thierolm/.evcc/evcc.db
[homematic] TRACE 2023/04/30 01:08:53 POST http://192.168.XXX.XXX:2010
[homematic] TRACE 2023/04/30 01:08:54 <?xml version="1.0" encoding="UTF-8"?>
<methodCall><methodName>setValue</methodName><params><param><value><string>0001DD89XXXXXX:3</string></value></param><param><value><string>STATE</string></value></param><param><value><boolean>0</boolean></value></param></params></methodCall>
--
<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value></value></param></params></methodResponse>
thierolm@LAPTOP-KQMMVN3H:~/git-repos/evcc$ ./evcc -c ./evcc_homematictest.yaml charger -l trace -e
[main  ] INFO 2023/04/30 01:09:03 evcc 0.116.5 (09c9154c)
[main  ] INFO 2023/04/30 01:09:03 using config file: ./evcc_homematictest.yaml
[db    ] INFO 2023/04/30 01:09:03 using sqlite database: /home/thierolm/.evcc/evcc.db
[homematic] TRACE 2023/04/30 01:09:03 POST http://192.168.XXX.XXX:2010
[homematic] TRACE 2023/04/30 01:09:04 <?xml version="1.0" encoding="UTF-8"?>
<methodCall><methodName>setValue</methodName><params><param><value><string>0001DD89XXXXXX:3</string></value></param><param><value><string>STATE</string></value></param><param><value><boolean>1</boolean></value></param></params></methodCall>
--
<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value></value></param></params></methodResponse>
thierolm@LAPTOP-KQMMVN3H:~/git-repos/evcc$ ./evcc -c ./evcc_homematictest.yaml charger -l trace
[main  ] INFO 2023/04/30 01:10:34 evcc 0.116.5 (09c9154c)
[main  ] INFO 2023/04/30 01:10:34 using config file: ./evcc_homematictest.yaml
[db    ] INFO 2023/04/30 01:10:34 using sqlite database: /home/thierolm/.evcc/evcc.db
[homematic] TRACE 2023/04/30 01:10:34 POST http://192.168.XXX.XXX:2010
[homematic] TRACE 2023/04/30 01:10:34 <?xml version="1.0" encoding="UTF-8"?>
<methodCall><methodName>getParamset</methodName><params><param><value><string>0001DD89XXXXXX:6</string></value></param><param><value><string>VALUES</string></value></param></params></methodCall>
--
<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><struct><member><name>VOLTAGE</name><value><double>231.1</double></value></member><member><name>POWER_STATUS</name><value><i4>0</i4></value></member><member><name>ENERGY_COUNTER</name><value><double>10890.0</double></value></member><member><name>CURRENT_STATUS</name><value><i4>0</i4></value></member><member><name>FREQUENCY</name><value><double>50.01</double></value></member><member><name>ENERGY_COUNTER_OVERFLOW</name><value><boolean>0</boolean></value></member><member><name>POWER</name><value><double>0.04</double></value></member><member><name>VOLTAGE_STATUS</name><value><i4>0</i4></value></member><member><name>CURRENT</name><value><double>0.0</double></value></member><member><name>FREQUENCY_STATUS</name><value><i4>0</i4></value></member></struct></value></param></params></methodResponse>
[homematic] TRACE 2023/04/30 01:10:34 POST http://192.168.XXX.XXX:2010
[homematic] TRACE 2023/04/30 01:10:34 <?xml version="1.0" encoding="UTF-8"?>
<methodCall><methodName>getParamset</methodName><params><param><value><string>0001DD89XXXXXX:3</string></value></param><param><value><string>VALUES</string></value></param></params></methodCall>
--
<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><struct><member><name>SECTION_STATUS</name><value><i4>0</i4></value></member><member><name>PROCESS</name><value><i4>0</i4></value></member><member><name>STATE</name><value><boolean>1</boolean></value></member><member><name>SECTION</name><value><i4>2</i4></value></member></struct></value></param></params></methodResponse>
Power:         0W
Energy:        10.9kWh
Charge status: B
Enabled:       true
Features:      []
thierolm@LAPTOP-KQMMVN3H:~/git-repos/evcc$ ./evcc -c ./evcc_homematictest.yaml meter -l trace
[main  ] INFO 2023/04/30 01:10:44 evcc 0.116.5 (09c9154c)
[main  ] INFO 2023/04/30 01:10:44 using config file: ./evcc_homematictest.yaml
[db    ] INFO 2023/04/30 01:10:44 using sqlite database: /home/thierolm/.evcc/evcc.db
grid1
-----
[homematic] TRACE 2023/04/30 01:10:44 POST http://192.168.XXX.XXX:2001
[homematic] TRACE 2023/04/30 01:10:44 <?xml version="1.0" encoding="UTF-8"?>
<methodCall><methodName>getParamset</methodName><params><param><value><string>SEQ3XXXXXX:1</string></value></param><param><value><string>VALUES</string></value></param></params></methodCall>
--
<?xml version="1.0" encoding="iso-8859-1"?>
<methodResponse><params><param>
        <value><struct><member><name>IEC_ENERGY_COUNTER</name><value><double>694.866100</double></value></member><member><name>IEC_POWER</name><value><double>0.000000</double></value></member></struct></value>
</param></params></methodResponse>
Power:  0W
Energy: 0.7kWh
```
thierolm: OK jetzt passt es:
```bash
thierolm@LAPTOP-KQMMVN3H:~/git-repos/evcc$ ./evcc -c ./evcc_homematictest.yaml meter -l debug
[main  ] INFO 2023/04/30 01:24:02 evcc 0.116.5 (09c9154c)
[main  ] INFO 2023/04/30 01:24:02 using config file: ./evcc_homematictest.yaml
[db    ] INFO 2023/04/30 01:24:02 using sqlite database: /home/thierolm/.evcc/evcc.db
grid1
-----
[homematic] TRACE 2023/04/30 01:24:02 POST http://192.168.XXX.XXX:2001
[homematic] TRACE 2023/04/30 01:24:02 <?xml version="1.0" encoding="UTF-8"?>
<methodCall><methodName>getParamset</methodName><params><param><value><string>SEQ3XXXXXX:1</string></value></param><param><value><string>VALUES</string></value></param></params></methodCall>
--
<?xml version="1.0" encoding="iso-8859-1"?>
<methodResponse><params><param>
        <value><struct><member><name>IEC_ENERGY_COUNTER</name><value><double>694.869400</double></value></member><member><name>IEC_POWER</name><value><double>82.560000</double></value></member></struct></value>
</param></params></methodResponse>
Power:  83W
Energy: 694.9kWh
```
thierolm: Bin immer wieder fasziniert davon, wie go-Code mit jeder Entwicklungs-Iteration einfacher und effizienter wird ...   🥇 
premultiply: Ich glaube da sind wir aber hier auch mit Andi auch an einen Meister geraten ;)

Patch:
diff --git a/charger/homematic.go b/charger/homematic.go
index aa06a644..30c547f3 100644
--- a/charger/homematic.go
+++ b/charger/homematic.go
@@ -1,6 +1,8 @@
 package charger
 
 import (
+	"time"
+
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/meter/homematic"
 	"github.com/evcc-io/evcc/util"
@@ -18,7 +20,7 @@ func init() {
 
 // NewCCUFromConfig creates a Homematic charger from generic config
 func NewCCUFromConfig(other map[string]interface{}) (api.Charger, error) {
-	var cc struct {
+	cc := struct {
 		embed         `mapstructure:",squash"`
 		URI           string
 		Device        string
@@ -27,18 +29,21 @@ func NewCCUFromConfig(other map[string]interface{}) (api.Charger, error) {
 		User          string
 		Password      string
 		StandbyPower  float64
+		Cache         time.Duration
+	}{
+		Cache: time.Second,
 	}
 
 	if err := util.DecodeOther(other, &cc); err != nil {
 		return nil, err
 	}
 
-	return NewCCU(cc.embed, cc.URI, cc.Device, cc.MeterChannel, cc.SwitchChannel, cc.User, cc.Password, cc.StandbyPower)
+	return NewCCU(cc.embed, cc.URI, cc.Device, cc.MeterChannel, cc.SwitchChannel, cc.User, cc.Password, cc.StandbyPower, cc.Cache)
 }
 
 // NewCCU creates a new connection with standbypower for charger
-func NewCCU(embed embed, uri, deviceid, meterid, switchid, user, password string, standbypower float64) (*CCU, error) {
-	conn, err := homematic.NewConnection(uri, deviceid, meterid, switchid, user, password)
+func NewCCU(embed embed, uri, deviceid, meterid, switchid, user, password string, standbypower float64, cache time.Duration) (*CCU, error) {
+	conn, err := homematic.NewConnection(uri, deviceid, meterid, switchid, user, password, cache)
 
 	c := &CCU{
 		conn: conn,
diff --git a/go.mod b/go.mod
index 92468fb4..b7ffc0a1 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/basgys/goxml2json v1.1.0
 	github.com/basvdlei/gotsmart v0.0.3
 	github.com/benbjohnson/clock v1.3.0
-	github.com/bogosj/tesla v1.1.0
+	github.com/bogosj/tesla v1.1.1-0.20230430222423-0d6e63ee90c9
 	github.com/cenkalti/backoff v2.2.1+incompatible
 	github.com/cjrd/allocate v0.0.0-20220510215731-986f24f0fb18
 	github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
diff --git a/go.sum b/go.sum
index ae40001a..df151fda 100644
--- a/go.sum
+++ b/go.sum
@@ -266,8 +266,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.1.0 h1:p+BOY8HGMT9GYOgvPM7Heu0d66TTkvDbVlVkErkbzDA=
-github.com/bogosj/tesla v1.1.0/go.mod h1:Rh1oHqNrnEVsKvdlOPELq4sLa6Swsuj+lbmZDBk1kfM=
+github.com/bogosj/tesla v1.1.1-0.20230430222423-0d6e63ee90c9 h1:Wd6BCHLS720MX1s2WHvu5Y7lo3HvRC/qONo7VbEbuac=
+github.com/bogosj/tesla v1.1.1-0.20230430222423-0d6e63ee90c9/go.mod h1:eF5996x0SVG6eslBpMA5kEjT1j9Q5gHO3lqovDmZKls=
 github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
 github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
@@ -568,8 +568,8 @@ github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK6
 github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
 github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
 github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
@@ -997,9 +997,9 @@ github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
+github.com/smartystreets/assertions v1.13.1 h1:Ef7KhSmjZcK6AVf9YbJdvPYG9avaF0ZxudX+ThRdWfU=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
+github.com/smartystreets/goconvey v1.8.0 h1:Oi49ha/2MURE0WexF052Z0m+BNSGirfjg5RL+JXWq3w=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
diff --git a/meter/homematic.go b/meter/homematic.go
index 0b417f9f..36aec499 100644
--- a/meter/homematic.go
+++ b/meter/homematic.go
@@ -1,6 +1,8 @@
 package meter
 
 import (
+	"time"
+
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/meter/homematic"
 	"github.com/evcc-io/evcc/util"
@@ -18,7 +20,7 @@ func init() {
 
 // NewCCUFromConfig creates a Homematic meter from generic config
 func NewCCUFromConfig(other map[string]interface{}) (api.Meter, error) {
-	var cc struct {
+	cc := struct {
 		URI           string
 		Device        string
 		MeterChannel  string
@@ -26,18 +28,21 @@ func NewCCUFromConfig(other map[string]interface{}) (api.Meter, error) {
 		User          string
 		Password      string
 		Usage         string
+		Cache         time.Duration
+	}{
+		Cache: time.Second,
 	}
 
 	if err := util.DecodeOther(other, &cc); err != nil {
 		return nil, err
 	}
 
-	return NewCCU(cc.URI, cc.Device, cc.MeterChannel, cc.SwitchChannel, cc.User, cc.Password, cc.Usage)
+	return NewCCU(cc.URI, cc.Device, cc.MeterChannel, cc.SwitchChannel, cc.User, cc.Password, cc.Usage, cc.Cache)
 }
 
 // NewCCU creates a new connection with usage for meter
-func NewCCU(uri, deviceid, meterid, switchid, user, password, usage string) (*CCU, error) {
-	conn, err := homematic.NewConnection(uri, deviceid, meterid, switchid, user, password)
+func NewCCU(uri, deviceid, meterid, switchid, user, password, usage string, cache time.Duration) (*CCU, error) {
+	conn, err := homematic.NewConnection(uri, deviceid, meterid, switchid, user, password, cache)
 
 	m := &CCU{
 		conn:  conn,
diff --git a/meter/homematic/connection.go b/meter/homematic/connection.go
index 30f0f854..691789dc 100644
--- a/meter/homematic/connection.go
+++ b/meter/homematic/connection.go
@@ -5,7 +5,9 @@ import (
 	"fmt"
 	"net/http"
 	"strings"
+	"time"
 
+	"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/transport"
@@ -18,6 +20,7 @@ import (
 // Homematic CCU settings
 type Settings struct {
 	URI, Device, MeterChannel, SwitchChannel, User, Password string
+	Cache                                                    time.Duration
 }
 
 // Connection is the Homematic CCU connection
@@ -25,10 +28,12 @@ type Connection struct {
 	log *util.Logger
 	*request.Helper
 	*Settings
+	meterCache  provider.Cacheable[MethodResponse]
+	switchCache provider.Cacheable[MethodResponse]
 }
 
 // NewConnection creates a new Homematic device connection.
-func NewConnection(uri, device, meterchannel, switchchannel, user, password string) (*Connection, error) {
+func NewConnection(uri, device, meterchannel, switchchannel, user, password string, cache time.Duration) (*Connection, error) {
 	log := util.NewLogger("homematic")
 
 	settings := &Settings{
@@ -36,6 +41,7 @@ func NewConnection(uri, device, meterchannel, switchchannel, user, password stri
 		Device:        device,
 		MeterChannel:  meterchannel,
 		SwitchChannel: switchchannel,
+		Cache:         cache,
 	}
 
 	conn := &Connection{
@@ -51,13 +57,82 @@ func NewConnection(uri, device, meterchannel, switchchannel, user, password stri
 		conn.Client.Transport = transport.BasicAuth(user, password, conn.Client.Transport)
 	}
 
-	if err := conn.Init(); err != nil {
-		return conn, err
-	}
+	conn.switchCache = provider.ResettableCached(func() (MethodResponse, error) {
+		return conn.XmlCmd("getParamset", conn.SwitchChannel, Param{CCUString: "VALUES"})
+	}, conn.Cache)
+
+	conn.meterCache = provider.ResettableCached(func() (MethodResponse, error) {
+		return conn.XmlCmd("getParamset", conn.MeterChannel, Param{CCUString: "VALUES"})
+	}, conn.Cache)
 
 	return conn, nil
 }
 
+// Enable sets the homematic HMIP-PSM switchchannel state to true=on/false=off
+func (c *Connection) Enable(enable bool) error {
+	onoff := map[bool]string{true: "1", false: "0"}
+	_, err := c.XmlCmd("setValue", c.SwitchChannel, Param{CCUString: "STATE"}, Param{CCUBool: onoff[enable]})
+	if err == nil {
+		c.switchCache.Reset()
+		c.meterCache.Reset()
+	}
+	return err
+}
+
+// Enabled reads the homematic HMIP-PSM switchchannel state true=on/false=off
+func (c *Connection) Enabled() (bool, error) {
+	res, err := c.switchCache.Get()
+	if err != nil {
+		return false, err
+	}
+	return getBoolValue(res, "STATE"), nil
+}
+
+// CurrentPower reads the homematic HMIP-PSM meterchannel power in W
+func (c *Connection) CurrentPower() (float64, error) {
+	res, err := c.meterCache.Get()
+	if err != nil {
+		return 0, err
+	}
+	return getFloatValue(res, "POWER"), nil
+}
+
+// TotalEnergy reads the homematic HMIP-PSM meterchannel energy in Wh
+func (c *Connection) TotalEnergy() (float64, error) {
+	res, err := c.meterCache.Get()
+	if err != nil {
+		return 0, err
+	}
+	return getFloatValue(res, "ENERGY_COUNTER") / 1e3, nil
+}
+
+// Currents reads the homematic HMIP-PSM meterchannel L1 current in A
+func (c *Connection) Currents() (float64, float64, float64, error) {
+	res, err := c.meterCache.Get()
+	if err != nil {
+		return 0, 0, 0, err
+	}
+	return getFloatValue(res, "CURRENT") / 1e3, 0, 0, nil
+}
+
+// GridCurrentPower reads the homematic HM-ES-TX-WM grid meterchannel power in W
+func (c *Connection) GridCurrentPower() (float64, error) {
+	res, err := c.meterCache.Get()
+	if err != nil {
+		return 0, err
+	}
+	return getFloatValue(res, "IEC_POWER"), nil
+}
+
+// GridTotalEnergy reads the homematic HM-ES-TX-WM grid meterchannel energy in kWh
+func (c *Connection) GridTotalEnergy() (float64, error) {
+	res, err := c.meterCache.Get()
+	if err != nil {
+		return 0, err
+	}
+	return getFloatValue(res, "IEC_ENERGY_COUNTER"), nil
+}
+
 func (c *Connection) XmlCmd(method, channel string, values ...Param) (MethodResponse, error) {
 	target := fmt.Sprintf("%s:%s", c.Device, channel)
 	hmc := MethodCall{
@@ -97,53 +172,26 @@ func (c *Connection) XmlCmd(method, channel string, values ...Param) (MethodResp
 	return hmr, parseError(hmr)
 }
 
-// Initialze CCU methods via system.listMethods call
-func (c *Connection) Init() error {
-	_, err := c.XmlCmd("system.listMethods", c.SwitchChannel)
-	return err
-}
-
-// Enabled reads the homematic HMIP-PSM switchchannel state true=on/false=off
-func (c *Connection) Enabled() (bool, error) {
-	res, err := c.XmlCmd("getValue", c.SwitchChannel, Param{CCUString: "STATE"})
-	return res.Value.CCUBool == "1", err
-}
-
-// Enable sets the homematic HMIP-PSM switchchannel state to true=on/false=off
-func (c *Connection) Enable(enable bool) error {
-	onoff := map[bool]string{true: "1", false: "0"}
-	_, err := c.XmlCmd("setValue", c.SwitchChannel, Param{CCUString: "STATE"}, Param{CCUBool: onoff[enable]})
-	return err
-}
-
-// CurrentPower reads the homematic HMIP-PSM meterchannel power in W
-func (c *Connection) CurrentPower() (float64, error) {
-	res, err := c.XmlCmd("getValue", c.MeterChannel, Param{CCUString: "POWER"})
-	return res.Value.CCUFloat, err
-}
+// getCCUFloat selects a float value of a CCU API response member
+func getFloatValue(res MethodResponse, valueName string) float64 {
+	for _, m := range res.Member {
+		if m.Name == valueName {
+			return m.Value.CCUFloat
+		}
+	}
 
-// TotalEnergy reads the homematic HMIP-PSM meterchannel energy in Wh
-func (c *Connection) TotalEnergy() (float64, error) {
-	res, err := c.XmlCmd("getValue", c.MeterChannel, Param{CCUString: "ENERGY_COUNTER"})
-	return res.Value.CCUFloat / 1000, err
+	return 0
 }
 
-// Currents reads the homematic HMIP-PSM meterchannel L1 current in A
-func (c *Connection) Currents() (float64, float64, float64, error) {
-	res, err := c.XmlCmd("getValue", c.MeterChannel, Param{CCUString: "CURRENT"})
-	return res.Value.CCUFloat / 1000, 0, 0, err
-}
-
-// GridCurrentPower reads the homematic HM-ES-TX-WM grid meterchannel power in W
-func (c *Connection) GridCurrentPower() (float64, error) {
-	res, err := c.XmlCmd("getValue", c.MeterChannel, Param{CCUString: "IEC_POWER"})
-	return res.Value.CCUFloat, err
-}
+// getCCUBool selects a float value of a CCU API response member
+func getBoolValue(res MethodResponse, valueName string) bool {
+	for _, m := range res.Member {
+		if m.Name == valueName {
+			return m.Value.CCUBool
+		}
+	}
 
-// GridTotalEnergy reads the homematic HM-ES-TX-WM grid meterchannel energy in Wh
-func (c *Connection) GridTotalEnergy() (float64, error) {
-	res, err := c.XmlCmd("getValue", c.MeterChannel, Param{CCUString: "IEC_ENERGY_COUNTER"})
-	return res.Value.CCUFloat, err
+	return false
 }
 
 // parseError checks on Homematic CCU error codes
@@ -162,6 +210,10 @@ func parseError(res MethodResponse) error {
 		}
 	}
 
+	if faultString == "" {
+		faultString = "Unknown Homematic API Error"
+	}
+
 	if faultCode != 0 {
 		return fmt.Errorf("%s (%v)", faultString, faultCode)
 	}
diff --git a/meter/homematic/types.go b/meter/homematic/types.go
index ee0d84ca..13464c30 100644
--- a/meter/homematic/types.go
+++ b/meter/homematic/types.go
@@ -8,32 +8,36 @@ import (
 // https://homematic-ip.com/sites/default/files/downloads/HM_XmlRpc_API.pdf
 // https://homematic-ip.com/sites/default/files/downloads/HMIP_XmlRpc_API_Addendum.pdf
 
-type Param struct {
-	CCUBool   string  `xml:"value>boolean,omitempty"`
-	CCUFloat  float64 `xml:"value>double,omitempty"`
-	CCUInt    int64   `xml:"value>i4,omitempty"`
-	CCUString string  `xml:"value>string,omitempty"`
-}
-
 type MethodCall struct {
 	XMLName    xml.Name `xml:"methodCall"`
 	MethodName string   `xml:"methodName"`
 	Params     []Param  `xml:"params>param,omitempty"`
 }
 
+type Param struct {
+	CCUBool   string  `xml:"value>boolean,omitempty"`
+	CCUFloat  float64 `xml:"value>double,omitempty"`
+	CCUInt    int64   `xml:"value>i4,omitempty"`
+	CCUString string  `xml:"value>string,omitempty"`
+}
 type Member struct {
-	Name  string     `xml:"name,omitempty"`
-	Value FaultValue `xml:"value,omitempty"`
+	Name  string `xml:"name,omitempty"`
+	Value Value  `xml:"value,omitempty"`
 }
 
-type FaultValue struct {
+type MethodResponse struct {
+	XMLName   xml.Name `xml:"methodResponse"`
+	CCUBool   string   `xml:"params>param>value>boolean,omitempty"`
+	CCUFloat  float64  `xml:"params>param>value>double,omitempty"`
+	CCUInt    int64    `xml:"params>param>value>i4,omitempty"`
+	CCUString string   `xml:"params>param>value>string,omitempty"`
+	Member    []Member `xml:"params>param>value>struct>member,omitempty"`
+	Fault     []Member `xml:"fault>value>struct>member,omitempty"`
+}
+type Value struct {
 	XMLName   xml.Name `xml:"value"`
 	CCUString string   `xml:",chardata"`
 	CCUInt    int64    `xml:"i4,omitempty"`
-}
-
-type MethodResponse struct {
-	XMLName xml.Name `xml:"methodResponse"`
-	Value   Param    `xml:"params>param,omitempty"`
-	Fault   []Member `xml:"fault>value>struct>member,omitempty"`
+	CCUBool   bool     `xml:"boolean,omitempty"`
+	CCUFloat  float64  `xml:"double,omitempty"`
 }
diff --git a/templates/definition/charger/elli-charger-connect.yaml b/templates/definition/charger/elli-charger-connect.yaml
index 46b6f978..2aba3073 100644
--- a/templates/definition/charger/elli-charger-connect.yaml
+++ b/templates/definition/charger/elli-charger-connect.yaml
@@ -2,8 +2,19 @@ template: elliconnect
 products:
   - brand: Elli
     description:
-      de: Charger Connect (ohne Meter) (Beta)
-      en: Charger Connect (without Meter) (Beta)
+      generic: Charger Connect (Beta)
+  - brand: Volkswagen
+    description:
+      generic: ID. Charger Connect (Beta)
+  - brand: Skoda
+    description:
+      generic: iV Charger Connect (Beta)
+  - brand: Elli
+    description:
+      generic: Charger Connect (Beta)
+  - brand: Audi
+    description:
+      generic: Wallbox plus (Beta)
 capabilities: ["mA"]
 requirements:
   evcc: ["eebus"]
@@ -14,12 +25,16 @@ requirements:
       Eine Identifikation des Fahrzeugs über die RFID Karte ist nicht möglich.
 
       Die Unterstützung ist im Beta Stadium und es kann noch zu Problemen kommen!
+
+      Hinweis: Wenn du deiner Wallbox nachträglich einen Energiezähler hinzugefügt hast, nutze bitte die Pro bzw. Connected+ Integration.
     en: |
       The device has to have a fix IP address (manuall or via DHCP).
 
       The identification of a vehicle using the RFID card is not possible.
 
       The support is in beta state and problems can occur!
+
+      Note: If you've added an energy meter to your charger please use the Pro or Connected+ integration.
 params:
   - preset: eebus
   - name: ip
diff --git a/templates/definition/charger/elli-charger-pro.yaml b/templates/definition/charger/elli-charger-pro.yaml
index d30be709..a6f11b7d 100644
--- a/templates/definition/charger/elli-charger-pro.yaml
+++ b/templates/definition/charger/elli-charger-pro.yaml
@@ -2,8 +2,19 @@ template: ellipro
 products:
   - brand: Elli
     description:
-      de: Charger Connect (mit Meter)/Pro (Beta)
-      en: Charger Connect (with Meter)/Pro (Beta)
+      generic: Charger Pro (Beta)
+  - brand: Volkswagen
+    description:
+      generic: ID. Charger Pro (Beta)
+  - brand: Skoda
+    description:
+      generic: iV Charger Connect+ (Beta)
+  - brand: Cupra
+    description:
+      generic: Charger Pro (Beta)
+  - brand: Audi
+    description:
+      generic: Audi Wallbox pro (Beta)
 capabilities: ["mA"]
 requirements:
   evcc: ["eebus"]
diff --git a/templates/docs/charger/elliconnect_0.yaml b/templates/docs/charger/elliconnect_0.yaml
index df80550c..0db51911 100644
--- a/templates/docs/charger/elliconnect_0.yaml
+++ b/templates/docs/charger/elliconnect_0.yaml
@@ -1,6 +1,6 @@
 product:
   brand: Elli
-  description: Charger Connect (ohne Meter) (Beta)
+  description: Charger Connect (Beta)
 capabilities: ["mA"]
 requirements: ["eebus"]
 description: |
@@ -10,6 +10,8 @@ description: |
 
   Die Unterstützung ist im Beta Stadium und es kann noch zu Problemen kommen!
 
+  Hinweis: Wenn du deiner Wallbox nachträglich einen Energiezähler hinzugefügt hast, nutze bitte die Pro bzw. Connected+ Integration.
+
 render:
   - default: |
       type: template
diff --git a/templates/docs/charger/elliconnect_1.yaml b/templates/docs/charger/elliconnect_1.yaml
new file mode 100644
index 00000000..f7f2619a
--- /dev/null
+++ b/templates/docs/charger/elliconnect_1.yaml
@@ -0,0 +1,20 @@
+product:
+  brand: Volkswagen
+  description: ID. Charger Connect (Beta)
+capabilities: ["mA"]
+requirements: ["eebus"]
+description: |
+  Dem Gerät muss eine feste IP Adresse zugewiesen sein (Manuell oder per DHCP).
+
+  Eine Identifikation des Fahrzeugs über die RFID Karte ist nicht möglich.
+
+  Die Unterstützung ist im Beta Stadium und es kann noch zu Problemen kommen!
+
+  Hinweis: Wenn du deiner Wallbox nachträglich einen Energiezähler hinzugefügt hast, nutze bitte die Pro bzw. Connected+ Integration.
+
+render:
+  - default: |
+      type: template
+      template: elliconnect
+      ski: # Die SKI der Wallbox, üblicherweise im Web Interface der Wallbox zu finden
+      ip: 192.0.2.2 # IP-Adresse
diff --git a/templates/docs/charger/elliconnect_2.yaml b/templates/docs/charger/elliconnect_2.yaml
new file mode 100644
index 00000000..3dcb1a7f
--- /dev/null
+++ b/templates/docs/charger/elliconnect_2.yaml
@@ -0,0 +1,20 @@
+product:
+  brand: Skoda
+  description: iV Charger Connect (Beta)
+capabilities: ["mA"]
+requirements: ["eebus"]
+description: |
+  Dem Gerät muss eine feste IP Adresse zugewiesen sein (Manuell oder per DHCP).
+
+  Eine Identifikation des Fahrzeugs über die RFID Karte ist nicht möglich.
+
+  Die Unterstützung ist im Beta Stadium und es kann noch zu Problemen kommen!
+
+  Hinweis: Wenn du deiner Wallbox nachträglich einen Energiezähler hinzugefügt hast, nutze bitte die Pro bzw. Connected+ Integration.
+
+render:
+  - default: |
+      type: template
+      template: elliconnect
+      ski: # Die SKI der Wallbox, üblicherweise im Web Interface der Wallbox zu finden
+      ip: 192.0.2.2 # IP-Adresse
diff --git a/templates/docs/charger/elliconnect_3.yaml b/templates/docs/charger/elliconnect_3.yaml
new file mode 100644
index 00000000..0db51911
--- /dev/null
+++ b/templates/docs/charger/elliconnect_3.yaml
@@ -0,0 +1,20 @@
+product:
+  brand: Elli
+  description: Charger Connect (Beta)
+capabilities: ["mA"]
+requirements: ["eebus"]
+description: |
+  Dem Gerät muss eine feste IP Adresse zugewiesen sein (Manuell oder per DHCP).
+
+  Eine Identifikation des Fahrzeugs über die RFID Karte ist nicht möglich.
+
+  Die Unterstützung ist im Beta Stadium und es kann noch zu Problemen kommen!
+
+  Hinweis: Wenn du deiner Wallbox nachträglich einen Energiezähler hinzugefügt hast, nutze bitte die Pro bzw. Connected+ Integration.
+
+render:
+  - default: |
+      type: template
+      template: elliconnect
+      ski: # Die SKI der Wallbox, üblicherweise im Web Interface der Wallbox zu finden
+      ip: 192.0.2.2 # IP-Adresse
diff --git a/templates/docs/charger/elliconnect_4.yaml b/templates/docs/charger/elliconnect_4.yaml
new file mode 100644
index 00000000..070abde3
--- /dev/null
+++ b/templates/docs/charger/elliconnect_4.yaml
@@ -0,0 +1,20 @@
+product:
+  brand: Audi
+  description: Wallbox plus (Beta)
+capabilities: ["mA"]
+requirements: ["eebus"]
+description: |
+  Dem Gerät muss eine feste IP Adresse zugewiesen sein (Manuell oder per DHCP).
+
+  Eine Identifikation des Fahrzeugs über die RFID Karte ist nicht möglich.
+
+  Die Unterstützung ist im Beta Stadium und es kann noch zu Problemen kommen!
+
+  Hinweis: Wenn du deiner Wallbox nachträglich einen Energiezähler hinzugefügt hast, nutze bitte die Pro bzw. Connected+ Integration.
+
+render:
+  - default: |
+      type: template
+      template: elliconnect
+      ski: # Die SKI der Wallbox, üblicherweise im Web Interface der Wallbox zu finden
+      ip: 192.0.2.2 # IP-Adresse
diff --git a/templates/docs/charger/ellipro_0.yaml b/templates/docs/charger/ellipro_0.yaml
index 4e527dd0..196da10e 100644
--- a/templates/docs/charger/ellipro_0.yaml
+++ b/templates/docs/charger/ellipro_0.yaml
@@ -1,6 +1,6 @@
 product:
   brand: Elli
-  description: Charger Connect (mit Meter)/Pro (Beta)
+  description: Charger Pro (Beta)
 capabilities: ["mA"]
 requirements: ["eebus"]
 description: |
diff --git a/templates/docs/charger/ellipro_1.yaml b/templates/docs/charger/ellipro_1.yaml
new file mode 100644
index 00000000..951700fa
--- /dev/null
+++ b/templates/docs/charger/ellipro_1.yaml
@@ -0,0 +1,18 @@
+product:
+  brand: Volkswagen
+  description: ID. Charger Pro (Beta)
+capabilities: ["mA"]
+requirements: ["eebus"]
+description: |
+  Dem Gerät muss eine feste IP Adresse zugewiesen sein (Manuell oder per DHCP).
+
+  Eine Identifikation des Fahrzeugs über die RFID Karte ist nicht möglich.
+
+  Die Unterstützung ist im Beta Stadium und es kann noch zu Problemen kommen!
+
+render:
+  - default: |
+      type: template
+      template: ellipro
+      ski: # Die SKI der Wallbox, üblicherweise im Web Interface der Wallbox zu finden
+      ip: 192.0.2.2 # IP-Adresse
diff --git a/templates/docs/charger/ellipro_2.yaml b/templates/docs/charger/ellipro_2.yaml
new file mode 100644
index 00000000..c5ad01e8
--- /dev/null
+++ b/templates/docs/charger/ellipro_2.yaml
@@ -0,0 +1,18 @@
+product:
+  brand: Skoda
+  description: iV Charger Connect+ (Beta)
+capabilities: ["mA"]
+requirements: ["eebus"]
+description: |
+  Dem Gerät muss eine feste IP Adresse zugewiesen sein (Manuell oder per DHCP).
+
+  Eine Identifikation des Fahrzeugs über die RFID Karte ist nicht möglich.
+
+  Die Unterstützung ist im Beta Stadium und es kann noch zu Problemen kommen!
+
+render:
+  - default: |
+      type: template
+      template: ellipro
+      ski: # Die SKI der Wallbox, üblicherweise im Web Interface der Wallbox zu finden
+      ip: 192.0.2.2 # IP-Adresse
diff --git a/templates/docs/charger/ellipro_3.yaml b/templates/docs/charger/ellipro_3.yaml
new file mode 100644
index 00000000..3167c13f
--- /dev/null
+++ b/templates/docs/charger/ellipro_3.yaml
@@ -0,0 +1,18 @@
+product:
+  brand: Cupra
+  description: Charger Pro (Beta)
+capabilities: ["mA"]
+requirements: ["eebus"]
+description: |
+  Dem Gerät muss eine feste IP Adresse zugewiesen sein (Manuell oder per DHCP).
+
+  Eine Identifikation des Fahrzeugs über die RFID Karte ist nicht möglich.
+
+  Die Unterstützung ist im Beta Stadium und es kann noch zu Problemen kommen!
+
+render:
+  - default: |
+      type: template
+      template: ellipro
+      ski: # Die SKI der Wallbox, üblicherweise im Web Interface der Wallbox zu finden
+      ip: 192.0.2.2 # IP-Adresse
diff --git a/templates/docs/charger/ellipro_4.yaml b/templates/docs/charger/ellipro_4.yaml
new file mode 100644
index 00000000..9c1abcfb
--- /dev/null
+++ b/templates/docs/charger/ellipro_4.yaml
@@ -0,0 +1,18 @@
+product:
+  brand: Audi
+  description: Audi Wallbox pro (Beta)
+capabilities: ["mA"]
+requirements: ["eebus"]
+description: |
+  Dem Gerät muss eine feste IP Adresse zugewiesen sein (Manuell oder per DHCP).
+
+  Eine Identifikation des Fahrzeugs über die RFID Karte ist nicht möglich.
+
+  Die Unterstützung ist im Beta Stadium und es kann noch zu Problemen kommen!
+
+render:
+  - default: |
+      type: template
+      template: ellipro
+      ski: # Die SKI der Wallbox, üblicherweise im Web Interface der Wallbox zu finden
+      ip: 192.0.2.2 # IP-Adresse
diff --git a/templates/evcc.io/brands.json b/templates/evcc.io/brands.json
index 5334dcd6..2c197136 100644
--- a/templates/evcc.io/brands.json
+++ b/templates/evcc.io/brands.json
@@ -4,10 +4,12 @@
   "ABL",
   "Alfen",
   "Alphatec",
+  "Audi",
   "Bender",
   "BMW",
   "cFos",
   "Compleo",
+  "Cupra",
   "Dadapower",
   "DaheimLaden",
   "E.ON Drive",
@@ -43,6 +45,7 @@
   "Schrack",
   "SENEC",
   "Siemens",
+  "Skoda",
   "SMA",
   "Sonnen",
   "Stark in Strom",
@@ -51,6 +54,7 @@
   "TinkerForge",
   "Ubitricity",
   "Vestel",
+  "Volkswagen",
   "Wallbe",
   "wallbox",
   "Walther Werke",
diff --git a/vehicle/tesla.go b/vehicle/tesla.go
index 9eee564e..2e2adff4 100644
--- a/vehicle/tesla.go
+++ b/vehicle/tesla.go
@@ -15,10 +15,8 @@ import (
 // Tesla is an api.Vehicle implementation for Tesla cars
 type Tesla struct {
 	*embed
-	vehicle       *tesla.Vehicle
-	chargeStateG  func() (*tesla.ChargeState, error)
-	vehicleStateG func() (*tesla.VehicleState, error)
-	driveStateG   func() (*tesla.DriveState, error)
+	vehicle *tesla.Vehicle
+	dataG   func() (*tesla.VehicleData, error)
 }
 
 func init() {
@@ -78,19 +76,17 @@ func NewTeslaFromConfig(other map[string]interface{}) (api.Vehicle, error) {
 		v.Title_ = v.vehicle.DisplayName
 	}
 
-	v.chargeStateG = provider.Cached(v.vehicle.ChargeState, cc.Cache)
-	v.vehicleStateG = provider.Cached(v.vehicle.VehicleState, cc.Cache)
-	v.driveStateG = provider.Cached(v.vehicle.DriveState, cc.Cache)
+	v.dataG = provider.Cached(v.vehicle.Data, cc.Cache)
 
 	return v, nil
 }
 
 // Soc implements the api.Vehicle interface
 func (v *Tesla) Soc() (float64, error) {
-	res, err := v.chargeStateG()
+	res, err := v.dataG()
 
 	if err == nil {
-		return float64(res.UsableBatteryLevel), nil
+		return float64(res.Response.ChargeState.UsableBatteryLevel), nil
 	}
 
 	return 0, err
@@ -101,31 +97,30 @@ var _ api.ChargeState = (*Tesla)(nil)
 // Status implements the api.ChargeState interface
 func (v *Tesla) Status() (api.ChargeStatus, error) {
 	status := api.StatusA // disconnected
-	res, err := v.chargeStateG()
+	res, err := v.dataG()
+	if err != nil {
+		return status, err
+	}
 
-	if err == nil {
-		if res.ChargingState == "Stopped" || res.ChargingState == "NoPower" || res.ChargingState == "Complete" {
-			status = api.StatusB
-		}
-		if res.ChargingState == "Charging" {
-			status = api.StatusC
-		}
+	switch res.Response.ChargeState.ChargingState {
+	case "Stopped", "NoPower", "Complete":
+		status = api.StatusB
+	case "Charging":
+		status = api.StatusC
 	}
 
-	return status, err
+	return status, nil
 }
 
 var _ api.ChargeRater = (*Tesla)(nil)
 
 // ChargedEnergy implements the api.ChargeRater interface
 func (v *Tesla) ChargedEnergy() (float64, error) {
-	res, err := v.chargeStateG()
-
-	if err == nil {
-		return res.ChargeEnergyAdded, nil
+	res, err := v.dataG()
+	if err != nil {
+		return 0, err
 	}
-
-	return 0, err
+	return res.Response.ChargeState.ChargeEnergyAdded, nil
 }
 
 const kmPerMile = 1.609344
@@ -134,42 +129,35 @@ var _ api.VehicleRange = (*Tesla)(nil)
 
 // Range implements the api.VehicleRange interface
 func (v *Tesla) Range() (int64, error) {
-	res, err := v.chargeStateG()
-
-	if err == nil {
-		// miles to km
-		return int64(kmPerMile * res.BatteryRange), nil
+	res, err := v.dataG()
+	if err != nil {
+		return 0, err
 	}
-
-	return 0, err
+	// miles to km
+	return int64(kmPerMile * res.Response.ChargeState.BatteryRange), nil
 }
 
 var _ api.VehicleOdometer = (*Tesla)(nil)
 
 // Odometer implements the api.VehicleOdometer interface
 func (v *Tesla) Odometer() (float64, error) {
-	res, err := v.vehicleStateG()
-
-	if err == nil {
-		// miles to km
-		return kmPerMile * res.Odometer, nil
+	res, err := v.dataG()
+	if err != nil {
+		return 0, err
 	}
-
-	return 0, err
+	// miles to km
+	return kmPerMile * res.Response.VehicleState.Odometer, nil
 }
 
 var _ api.VehicleFinishTimer = (*Tesla)(nil)
 
 // FinishTime implements the api.VehicleFinishTimer interface
 func (v *Tesla) FinishTime() (time.Time, error) {
-	res, err := v.chargeStateG()
-
-	if err == nil {
-		t := time.Now()
-		return t.Add(time.Duration(res.MinutesToFullCharge) * time.Minute), err
+	res, err := v.dataG()
+	if err != nil {
+		return time.Time{}, err
 	}
-
-	return time.Time{}, err
+	return time.Now().Add(time.Duration(res.Response.ChargeState.MinutesToFullCharge) * time.Minute), nil
 }
 
 // TODO api.Climater implementation has been removed as it drains battery. Re-check at a later time.
@@ -178,24 +166,22 @@ var _ api.VehiclePosition = (*Tesla)(nil)
 
 // Position implements the api.VehiclePosition interface
 func (v *Tesla) Position() (float64, float64, error) {
-	res, err := v.driveStateG()
-	if err == nil {
-		return res.Latitude, res.Longitude, nil
+	res, err := v.dataG()
+	if err != nil {
+		return 0, 0, err
 	}
-
-	return 0, 0, err
+	return res.Response.DriveState.Latitude, res.Response.DriveState.Longitude, nil
 }
 
 var _ api.SocLimiter = (*Tesla)(nil)
 
 // TargetSoc implements the api.SocLimiter interface
 func (v *Tesla) TargetSoc() (float64, error) {
-	res, err := v.chargeStateG()
-	if err == nil {
-		return float64(res.ChargeLimitSoc), nil
+	res, err := v.dataG()
+	if err != nil {
+		return 0, err
 	}
-
-	return 0, err
+	return float64(res.Response.ChargeState.ChargeLimitSoc), nil
 }
 
 var _ api.CurrentLimiter = (*Tesla)(nil)


Test patch:
diff --git a/meter/homematic/types_test.go b/meter/homematic/types_test.go
index 9d999248..ac60e93c 100644
--- a/meter/homematic/types_test.go
+++ b/meter/homematic/types_test.go
@@ -12,13 +12,38 @@ import (
 func TestUnmarshalMethodResponse(t *testing.T) {
 
 	{
-		// Double response test
+		// BidCos-RF (Port 2001) getParamset measure-channel response test
 		var res MethodResponse
 
-		xmlstr := `<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><double>20698.0</double></value></param></params></methodResponse>`
+		xmlstr := `<?xml version="1.0" encoding="iso-8859-1"?><methodResponse><params><param><value><struct><member><name>IEC_ENERGY_COUNTER</name><value><double>689.586500</double></value></member><member><name>IEC_POWER</name><value><double>166.390000</double></value></member></struct></value></param></params></methodResponse>`
+		assert.NoError(t, xml.Unmarshal([]byte(strings.Replace(string(xmlstr), "iso-8859-1", "UTF-8", 1)), &res))
+
+		assert.Equal(t, "IEC_ENERGY_COUNTER", res.Member[0].Name)
+		assert.Equal(t, float64(689.586500), res.Member[0].Value.CCUFloat)
+	}
+
+	{
+		// BidCos-IP (Port 2010) getParamset measure-channel response test
+		var res MethodResponse
+
+		xmlstr := `<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><struct><member><name>VOLTAGE</name><value><double>230.6</double></value></member><member><name>POWER_STATUS</name><value><i4>0</i4></value></member><member><name>ENERGY_COUNTER</name><value><double>10888.7</double></value></member><member><name>CURRENT_STATUS</name><value><i4>0</i4></value></member><member><name>FREQUENCY</name><value><double>49.97</double></value></member><member><name>ENERGY_COUNTER_OVERFLOW</name><value><boolean>0</boolean></value></member><member><name>POWER</name><value><double>0.05</double></value></member><member><name>VOLTAGE_STATUS</name><value><i4>0</i4></value></member><member><name>CURRENT</name><value><double>0.0</double></value></member><member><name>FREQUENCY_STATUS</name><value><i4>0</i4></value></member></struct></value></param></params></methodResponse>`
+		assert.NoError(t, xml.Unmarshal([]byte(strings.Replace(string(xmlstr), "ISO-8859-1", "UTF-8", 1)), &res))
+
+		assert.Equal(t, "ENERGY_COUNTER", res.Member[2].Name)
+		assert.Equal(t, float64(10888.7), res.Member[2].Value.CCUFloat)
+		assert.Equal(t, "POWER", res.Member[6].Name)
+		assert.Equal(t, float64(0.05), res.Member[6].Value.CCUFloat)
+	}
+
+	{
+		// BidCos-IP (Port 2010) getParamset switch-channel response test
+		var res MethodResponse
+
+		xmlstr := `<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><struct><member><name>SECTION_STATUS</name><value><i4>0</i4></value></member><member><name>PROCESS</name><value><i4>0</i4></value></member><member><name>STATE</name><value><boolean>1</boolean></value></member><member><name>SECTION</name><value><i4>2</i4></value></member></struct></value></param></params></methodResponse>`
 		assert.NoError(t, xml.Unmarshal([]byte(strings.Replace(string(xmlstr), "ISO-8859-1", "UTF-8", 1)), &res))
 
-		assert.Equal(t, float64(20698), res.Value.CCUFloat)
+		assert.Equal(t, "STATE", res.Member[2].Name)
+		assert.Equal(t, true, res.Member[2].Value.CCUBool)
 	}
 
 	{