Repository: evcc-io/evcc (go)

Issue:
Value Energy wrong for Shelly EM with template shelly-1pm
### Describe the bug

See discussion https://github.com/evcc-io/evcc/discussions/6089

Using a Shelly EM (the simple 2 Channel version) with the template shelly-1pm will give the correct "power" value, but the wrong "energy" value

![image](https://user-images.githubusercontent.com/90329895/217178543-e1edc8d3-bd39-426e-817b-f35d0b44c60b.png)


If I use the template shelly-3em the values "power" and "energy" are correct, but of course there are errors because of missing values, the simple EM can not provide compared to 3EM

![image](https://user-images.githubusercontent.com/90329895/217178877-84234dce-52ea-40d4-b069-47bca42c88cb.png)


Shelly Output:

Shelly Pro 1PM http://192.168.178.64/rpc/shelly.getstatus
[Shelly1PM_status.txt](https://github.com/evcc-io/evcc/files/10672556/Shelly1PM_status.txt)

Shelly EM http://192.168.178.65/status
[ShellyEM_status.txt](https://github.com/evcc-io/evcc/files/10672557/ShellyEM_status.txt)

Solution could be a shelly-em template like shelly-3em without the values for "Current L1...L3", "Voltage L1...L3" and "Power L1...L3"?


### Steps to reproduce

1. create loadpoint using Shelly EM as meter
2. use shelly-1pm template -> get wrong/no energy value
3. use shelly-3em template -> get correct energy value, but of course errors for missing values



### Configuration details

```yaml
uri: 192.168.178.70:7070
interval: 10s

# log settings
log: error
levels:
  core: debug
  lp-1: debug
  lp-2: debug

# meter definitions
meters:
- name: grid
  type: custom
  power:
    source: modbus
    uri: 192.168.178.61:502
    id: 1
    register:
      address: 40073
      type: holding
      decode: int32s
- name: pv
  type: custom
  power:
    source: modbus
    uri: 192.168.178.61:502
    id: 1
    register:
      address: 40067
      type: holding
      decode: int32s
- name: battery
  type: custom
  power:
    source: modbus
    uri: 192.168.178.61:502
    id: 1
    register:
      address: 40069
      type: holding
      decode: int32s
    scale: -1
  soc:
    source: modbus
    uri: 192.168.178.61:502
    id: 1
    register:
      address: 40082
      type: holding
      decode: uint16
- name: wallbox
  type: sma
  uri: 192.168.178.63
- name: shelly_m_heizstab
  type: template
  template: shelly-1pm
  usage: charge
  host: 192.168.178.65
#  user: ***
#  password: ***
#  channel: 0

# charger definitions
chargers:
- name: wallbe
  type: wallbe
  uri: 192.168.178.62:502
- name: shelly_s_heizstab
  type: template
  template: shelly
  host: 192.168.178.64
  standbypower: -5

# vehicle definitions
vehicles:
- name: hyundai
  type: hyundai
  title: Ioniq
  capacity: 38
  phases: 1
  user: ***
  password: ***
  onIdentify:
    mode: pv

- name: renault
  type: renault
  title: Twingo
  user: ***
  password: ***
  capacity: 22
  onIdentify:
    mode: pv
    targetSoC: 80

- name: heizen
  type: custom
  title: Heizstab
  phases: 1
  soc:
    source: js
    script: 50;

# site definition
site:
- title: Home
  meters:
    grid: grid
    pvs:
    - pv
    batteries:
    - battery
  prioritySoC: 30

# loadpoint definition
loadpoints:
- title: Garage
  charger: wallbe
  meter: wallbox
  vehicles:
  - hyundai
  - renault
  mode: pv
  resetOnDisconnect: true
  soc:
    poll:
      mode: connected
      interval: 60m
    estimate: true
  phases: 1
  enable:
    delay: 1m
    threshold: 0
  disable:
    delay: 5m
    threshold: 0
  guardduration: 5m
  mincurrent: 6
  maxcurrent: 16
- title: Heizstab
  charger: shelly_s_heizstab
  meter: shelly_m_heizstab
  vehicles: heizen
  mode: pv
  resetOnDisconnect: true
  phases: 1
  mincurrent: 13
  maxcurrent: 14
  enable:
    threshold: 0
    delay: 30s
  disable:
    delay: 15s
    threshold: 0

# tariff definition
tariffs:
  currency: EUR
  grid:
    type: fixed
    price: 0.2923
  feedin:
    type: fixed
    price: 0.07
```


### Log details

```text
[main  ] INFO 2023/02/07 08:25:37 evcc 0.112.4
[main  ] INFO 2023/02/07 08:25:37 using config file: /etc/evcc.yaml
[main  ] WARN 2023/02/07 08:25:37 `uri` is deprecated and will be ignored. Use `network` instead.
[main  ] INFO 2023/02/07 08:25:37 starting ui and api at :7070
[db    ] INFO 2023/02/07 08:25:37 using sqlite database: /root/.evcc/evcc.db
[lp-1  ] WARN 2023/02/07 08:25:41 poll mode '{connected 1h0m0s}' may deplete your battery or lead to API misuse. USE AT YOUR OWN RISK.
[lp-1  ] WARN 2023/02/07 08:25:41 vehicles option is deprecated
[lp-2  ] WARN 2023/02/07 08:25:41 vehicles option is deprecated
[site  ] WARN 2023/02/07 08:25:41 vehicle 'Heizstab' does not support automatic detection
[site  ] INFO 2023/02/07 08:25:41 site config:
[site  ] INFO 2023/02/07 08:25:41   meters:      grid ✓ pv ✓ battery ✓
[site  ] INFO 2023/02/07 08:25:41     grid:      power ✓ energy ✗ currents ✗
[site  ] INFO 2023/02/07 08:25:41     pv 1:      power ✓ energy ✗ currents ✗
[site  ] INFO 2023/02/07 08:25:41     battery 1: power ✓ energy ✗ currents ✗ soc ✓ capacity ✗
[site  ] INFO 2023/02/07 08:25:41   vehicles:
[site  ] INFO 2023/02/07 08:25:41     vehicle 1: range ✗ finish ✗ status ✗ climate ✗ wakeup ✗
[site  ] INFO 2023/02/07 08:25:41     vehicle 2: range ✓ finish ✓ status ✓ climate ✗ wakeup ✓
[site  ] INFO 2023/02/07 08:25:41     vehicle 3: range ✓ finish ✓ status ✓ climate ✓ wakeup ✓
[lp-1  ] INFO 2023/02/07 08:25:41 loadpoint 1:
[lp-1  ] INFO 2023/02/07 08:25:41   mode:        pv
[lp-1  ] INFO 2023/02/07 08:25:41   charger:     power ✗ energy ✗ currents ✗ phases ✗ wakeup ✗
[lp-1  ] INFO 2023/02/07 08:25:41   meters:      charge ✓
[lp-1  ] INFO 2023/02/07 08:25:41     charge:    power ✓ energy ✓ currents ✓
[lp-2  ] INFO 2023/02/07 08:25:41 loadpoint 2:
[lp-2  ] INFO 2023/02/07 08:25:41   mode:        pv
[lp-2  ] INFO 2023/02/07 08:25:41   charger:     power ✓ energy ✓ currents ✗ phases ✗ wakeup ✗
[lp-2  ] INFO 2023/02/07 08:25:41   meters:      charge ✓
[lp-2  ] INFO 2023/02/07 08:25:41     charge:    power ✓ energy ✓ currents ✗
[lp-1  ] DEBUG 2023/02/07 08:25:41 phase timer inactive
[lp-1  ] DEBUG 2023/02/07 08:25:41 pv timer inactive
[lp-2  ] DEBUG 2023/02/07 08:25:41 phase timer inactive
[lp-2  ] DEBUG 2023/02/07 08:25:41 pv timer inactive
[site  ] DEBUG 2023/02/07 08:25:41 ----
[lp-1  ] DEBUG 2023/02/07 08:25:41 charge power: 6W
[main  ] FATAL 2023/02/07 08:25:41 could not open port- check that evcc is not already running (listen tcp :7070: bind: address already in use)
```


### What type of operating system are you running?

Linux

### Version

evcc version 0.112.4

Pull request:
Shelly: Correct Total Energy 4 gen1 EM devices
Fixes #6113

including new Shelly JSON tests.

PR tested succesful with

1. Shelly 1
2. Shelly 1PM
3. Shelly Plus 1PM

Patch:
diff --git a/meter/shelly/connection.go b/meter/shelly/connection.go
index 191ee7c0..b618da83 100644
--- a/meter/shelly/connection.go
+++ b/meter/shelly/connection.go
@@ -15,9 +15,10 @@ import (
 // Connection is the Shelly connection
 type Connection struct {
 	*request.Helper
-	uri     string
-	channel int
-	gen     int // Shelly api generation
+	uri        string
+	channel    int
+	gen        int    // Shelly api generation
+	devicetype string // Shelly device type
 }
 
 // NewConnection creates a new Shelly device connection.
@@ -40,9 +41,10 @@ func NewConnection(uri, user, password string, channel int) (*Connection, error)
 	}
 
 	conn := &Connection{
-		Helper:  client,
-		channel: channel,
-		gen:     resp.Gen,
+		Helper:     client,
+		channel:    channel,
+		gen:        resp.Gen,
+		devicetype: resp.Type,
 	}
 
 	conn.Client.Transport = request.NewTripper(log, transport.Insecure())
@@ -184,13 +186,15 @@ func (d *Connection) TotalEnergy() (float64, error) {
 
 		switch {
 		case d.channel < len(res.Meters):
-			energy = res.Meters[d.channel].Total / 60
+			energy = res.Meters[d.channel].Total
 		case d.channel < len(res.EMeters):
-			energy = res.EMeters[d.channel].Total / 60
+			energy = res.EMeters[d.channel].Total
 		default:
 			return 0, errors.New("invalid channel, missing power meter")
 		}
 
+		energy = GetGen1Energy(d.devicetype, energy)
+
 	default:
 		var res Gen2StatusResponse
 		if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil {
@@ -209,3 +213,12 @@ func (d *Connection) TotalEnergy() (float64, error) {
 
 	return energy / 1000, nil
 }
+
+// GetGen1Energy in kWh
+func GetGen1Energy(devicetype string, energy float64) float64 {
+	// Gen 1 Shelly EM devices are providing Watt hours, Shelly EM devices are providing Watt minutes
+	if !strings.Contains(devicetype, "EM") {
+		energy = energy / 60
+	}
+	return energy
+}


Test patch:
diff --git a/meter/shelly/types_test.go b/meter/shelly/types_test.go
new file mode 100644
index 00000000..d2c23b83
--- /dev/null
+++ b/meter/shelly/types_test.go
@@ -0,0 +1,67 @@
+package shelly
+
+import (
+	"encoding/json"
+	"testing"
+)
+
+// Test Gen1StatusResponse response
+func TestUnmarshalGen1StatusResponse(t *testing.T) {
+	var res Gen1StatusResponse
+
+	// Shelly 1 PM channel 0 (1)
+	jsonstr := `{"wifi_sta":{"connected":true,"ssid":"XXXX-WLAN","ip":"192.168.178.XXX","rssi":-54},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"17:59","unixtime":1676134770,"serial":2437,"has_update":true,"mac":"84CCA8XXXXXXX","cfg_changed_cnt":1,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"overpower":false,"source":"http"}],"meters":[{"power":4711.12,"overpower":0.00,"is_valid":true,"timestamp":1676138370,"counters":[0.000, 0.000, 0.000],"total":6472513}],"inputs":[{"input":0,"event":"","event_cnt":0}],"temperature":16.79,"overtemperature":false,"tmp":{"tC":16.79,"tF":62.22, "is_valid":true},"temperature_status":"Normal","ext_sensors":{},"ext_temperature":{},"ext_humidity":{},"update":{"status":"pending","has_update":true,"new_version":"20221108-153925/v1.12.1-1PM-fix-g2821131","old_version":"20220209-094317/v1.11.8-g8c7bb8d"},"ram_total":50456,"ram_free":37056,"fs_size":233681,"fs_free":149094,"uptime":17284290}`
+	if err := json.Unmarshal([]byte(jsonstr), &res); err != nil {
+		t.Error(err)
+	}
+	if GetGen1Energy("SHSW-PM", res.Meters[0].Total) != 107875.21666666666 {
+		t.Error("SHSW-PM res.Meters[0].Total) != 107875.21666666666")
+	}
+	if res.Meters[0].Power != 4711.12 {
+		t.Error("res.Meters[0].Power != 4711.12")
+	}
+
+	// Shelly 1 channel 0 (1)
+	res = Gen1StatusResponse{}
+	jsonstr = `{"wifi_sta":{"connected":true,"ssid":"XXXX-WLAN","ip":"192.168.178.XXX","rssi":-57},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"19:25","unixtime":1676139913,"serial":959,"has_update":true,"mac":"E8DB8XXXXXX","cfg_changed_cnt":1,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"source":"timer"}],"meters":[{"power":81.5,"is_valid":true}],"inputs":[{"input":0,"event":"","event_cnt":0}],"ext_sensors":{},"ext_temperature":{},"ext_humidity":{},"update":{"status":"pending","has_update":true,"new_version":"20221027-091427/v1.12.1-ga9117d3","old_version":"20211109-124958/v1.11.7-g682a0db"},"ram_total":50880,"ram_free":38796,"fs_size":233681,"fs_free":151102,"uptime":20319391}`
+	if err := json.Unmarshal([]byte(jsonstr), &res); err != nil {
+		t.Error(err)
+	}
+	if GetGen1Energy("SHSW-1", res.Meters[0].Total) != 0 {
+		t.Error("SHSW-1 res.Meters[0].Total) != 0")
+	}
+	if res.Meters[0].Power != 81.5 {
+		t.Error("res.Meters[0].Power != -81.5")
+	}
+
+	// Shelly EM channel 0 (1)
+	res = Gen1StatusResponse{}
+	jsonstr = `{"wifi_sta":{"connected":true,"ssid":"XXXX","ip":"192.168.178.XXX","rssi":-55},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"11:16","unixtime":1676110566,"serial":21580,"has_update":false,"mac":"C45BXXXXX","cfg_changed_cnt":0,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"overpower":false,"is_valid":true,"source":"input"}],"emeters":[{"power":-620.34,"reactive":714.48,"pf":-0.66,"voltage":235.68,"is_valid":true,"total":401472.9,"total_returned":653673.7},{"power":0.00,"reactive":0.00,"pf":0.00,"voltage":235.68,"is_valid":true,"total":173411.3,"total_returned":294.2}],"update":{"status":"idle","has_update":false,"new_version":"20221027-105518/v1.12.1-ga9117d3","old_version":"20221027-105518/v1.12.1-ga9117d3"},"ram_total":51072,"ram_free":35660,"fs_size":233681,"fs_free":156373,"uptime":2226140}`
+	if err := json.Unmarshal([]byte(jsonstr), &res); err != nil {
+		t.Error(err)
+	}
+	if GetGen1Energy("SHEM", res.EMeters[0].Total) != 401472.9 {
+		t.Error("SHEM res.EMeters[0].Total) != 401472.9")
+	}
+	if res.EMeters[0].Power != -620.34 {
+		t.Error("res.EMeters[0].Power != -620.34")
+	}
+
+}
+
+// Test Gen2StatusResponse response
+func TestUnmarshalGen2StatusResponse(t *testing.T) {
+	var res Gen2StatusResponse
+
+	// Shelly Pro 1PM channel 0 (1)
+	jsonstr := `{"ble":{},"cloud":{"connected":true},"eth":{"ip":null},"input:0":{"id":0,"state":false},"input:1":{"id":1,"state":false},"mqtt":{"connected":false},"switch:0":{"id":0, "source":"HTTP", "output":false, "apower":47.11, "voltage":232.0, "current":0.000, "pf":0.00, "aenergy":{"total":5.125,"by_minute":[0.000,0.000,0.000],"minute_ts":1675718520},"temperature":{"tC":25.3, "tF":77.5}},"sys":{"mac":"30C6F78BB4D8","restart_required":false,"time":"22:22","unixtime":1675718522,"uptime":45070,"ram_size":234204,"ram_free":137716,"fs_size":524288,"fs_free":172032,"cfg_rev":13,"kvs_rev":1,"schedule_rev":0,"webhook_rev":0,"available_updates":{"beta":{"version":"0.13.0-beta3"}}},"wifi":{"sta_ip":"192.168.178.64","status":"got ip","ssid":"***","rssi":-62},"ws":{"connected":false}}`
+	if err := json.Unmarshal([]byte(jsonstr), &res); err != nil {
+		t.Error(err)
+	}
+	if res.Switch0.Aenergy.Total != 5.125 {
+		t.Error("res.Switch0.Aenergy.Total != 5.125")
+	}
+	if res.Switch0.Apower != 47.11 {
+		t.Error("res.Switch0.Apower != 47.11")
+	}
+}