From 6f5c0b64786ae8a75f83b8a67ac01ac07d8fba29 Mon Sep 17 00:00:00 2001 From: Markus Thierolf <77847348+thierolm@users.noreply.github.com> Date: Thu, 27 Mar 2025 11:10:24 +0100 Subject: [PATCH] Shelly: improve channel support/ standardise Pro EM readings (#20141) --- meter/shelly/energymeter.go | 8 ++++---- meter/shelly/switch.go | 34 ++++++++++------------------------ meter/shelly/types.go | 18 ++++++++---------- meter/shelly/types_test.go | 22 +++++++++++++++++++++- 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/meter/shelly/energymeter.go b/meter/shelly/energymeter.go index ccb25cb5e..47a70ac72 100644 --- a/meter/shelly/energymeter.go +++ b/meter/shelly/energymeter.go @@ -16,7 +16,7 @@ func NewEnergyMeter(conn *Connection) *EnergyMeter { // CurrentPower implements the api.Meter interface func (sh *EnergyMeter) CurrentPower() (float64, error) { - var res Gen2EmStatusResponse + var res Gen2StatusResponse if err := sh.Connection.execGen2Cmd("EM.GetStatus", false, &res); err != nil { return 0, err } @@ -38,7 +38,7 @@ var _ api.PhaseCurrents = (*EnergyMeter)(nil) // Currents implements the api.PhaseCurrents interface func (sh *EnergyMeter) Currents() (float64, float64, float64, error) { - var res Gen2EmStatusResponse + var res Gen2StatusResponse if err := sh.Connection.execGen2Cmd("EM.GetStatus", false, &res); err != nil { return 0, 0, 0, err } @@ -50,7 +50,7 @@ var _ api.PhaseVoltages = (*EnergyMeter)(nil) // Voltages implements the api.PhaseVoltages interface func (sh *EnergyMeter) Voltages() (float64, float64, float64, error) { - var res Gen2EmStatusResponse + var res Gen2StatusResponse if err := sh.Connection.execGen2Cmd("EM.GetStatus", false, &res); err != nil { return 0, 0, 0, err } @@ -62,7 +62,7 @@ var _ api.PhasePowers = (*EnergyMeter)(nil) // Powers implements the api.PhasePowers interface func (sh *EnergyMeter) Powers() (float64, float64, float64, error) { - var res Gen2EmStatusResponse + var res Gen2StatusResponse if err := sh.Connection.execGen2Cmd("EM.GetStatus", false, &res); err != nil { return 0, 0, 0, err } diff --git a/meter/shelly/switch.go b/meter/shelly/switch.go index 26795a9c6..59cda1a56 100644 --- a/meter/shelly/switch.go +++ b/meter/shelly/switch.go @@ -42,25 +42,18 @@ func (sh *Switch) CurrentPower() (float64, error) { } default: - var resem Gen2EmStatusResponse var res Gen2StatusResponse - if d.app == "Pro3EM" && d.profile == "monophase" { - if err := d.execGen2Cmd("Shelly.GetStatus", false, &resem); err != nil { - return 0, err - } - } else { - if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil { - return 0, err - } + if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil { + return 0, err } switch d.channel { case 1: - power = res.Switch1.Apower + res.Pm1.Apower + resem.Em1.ActPower + power = res.Switch1.Apower + res.Pm1.Apower + res.Em1.ActPower case 2: - power = res.Switch2.Apower + res.Pm2.Apower + resem.Em2.ActPower + power = res.Switch2.Apower + res.Pm2.Apower + res.Em2.ActPower default: - power = res.Switch0.Apower + res.Pm0.Apower + resem.Em0.ActPower + power = res.Switch0.Apower + res.Pm0.Apower + res.Em0.ActPower } } @@ -130,25 +123,18 @@ func (sh *Switch) TotalEnergy() (float64, error) { energy = gen1Energy(d.devicetype, energy) default: - var resem Gen2EmStatusResponse var res Gen2StatusResponse - if d.app == "Pro3EM" && d.profile == "monophase" { - if err := d.execGen2Cmd("Shelly.GetStatus", false, &resem); err != nil { - return 0, err - } - } else { - if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil { - return 0, err - } + if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil { + return 0, err } switch d.channel { case 1: - energy = res.Switch1.Aenergy.Total + res.Pm1.Aenergy.Total + resem.Em1Data.TotalActEnergy - resem.Em1Data.TotalActRetEnergy + energy = res.Switch1.Aenergy.Total + res.Pm1.Aenergy.Total + res.Em1Data.TotalActEnergy - res.Em1Data.TotalActRetEnergy case 2: - energy = res.Switch2.Aenergy.Total + res.Pm2.Aenergy.Total + resem.Em2Data.TotalActEnergy - resem.Em2Data.TotalActRetEnergy + energy = res.Switch2.Aenergy.Total + res.Pm2.Aenergy.Total + res.Em2Data.TotalActEnergy - res.Em2Data.TotalActRetEnergy default: - energy = res.Switch0.Aenergy.Total + res.Pm0.Aenergy.Total + resem.Em0Data.TotalActEnergy - resem.Em0Data.TotalActRetEnergy + energy = res.Switch0.Aenergy.Total + res.Pm0.Aenergy.Total + res.Em0Data.TotalActEnergy - res.Em0Data.TotalActRetEnergy } } diff --git a/meter/shelly/types.go b/meter/shelly/types.go index 5824d9811..52d3d4701 100644 --- a/meter/shelly/types.go +++ b/meter/shelly/types.go @@ -33,15 +33,6 @@ type Gen2Switch struct { } } -type Gen2StatusResponse struct { - Switch0 Gen2Switch `json:"switch:0"` - Switch1 Gen2Switch `json:"switch:1"` - Switch2 Gen2Switch `json:"switch:2"` - Pm0 Gen2Switch `json:"pm1:0"` - Pm1 Gen2Switch `json:"pm2:1"` - Pm2 Gen2Switch `json:"pm3:2"` -} - type Gen2Em struct { Current float64 `json:"current"` Voltage float64 `json:"voltage"` @@ -53,7 +44,14 @@ type Gen2EmData struct { TotalActRetEnergy float64 `json:"total_act_ret_energy"` } -type Gen2EmStatusResponse struct { +type Gen2StatusResponse struct { + Switch0 Gen2Switch `json:"switch:0"` + Switch1 Gen2Switch `json:"switch:1"` + Switch2 Gen2Switch `json:"switch:2"` + Pm0 Gen2Switch `json:"pm1:0"` + Pm1 Gen2Switch `json:"pm2:1"` + Pm2 Gen2Switch `json:"pm3:2"` + // additional shelly Pro EM meter JSON response TotalPower float64 `json:"total_act_power"` CurrentA float64 `json:"a_current"` CurrentB float64 `json:"b_current"` diff --git a/meter/shelly/types_test.go b/meter/shelly/types_test.go index 2e4e968e8..1a93e5ca3 100644 --- a/meter/shelly/types_test.go +++ b/meter/shelly/types_test.go @@ -70,7 +70,7 @@ func TestUnmarshalGen2StatusResponse(t *testing.T) { { // Shelly Pro 3EM - var res Gen2EmStatusResponse + var res Gen2StatusResponse jsonstr := `{"ble":{},"bthome":{"errors":["bluetooth_disabled"]},"cloud":{"connected":true},"em1:0":{"id":0,"current":3.705,"voltage":242.8,"act_power":598.9,"aprt_power":900.6,"pf":0.66,"freq":50.0,"calibration":"factory"},"em1:1":{"id":1,"current":0.194,"voltage":242.8,"act_power":0.0,"aprt_power":47.2,"pf":0.00,"freq":50.0,"calibration":"factory"},"em1:2":{"id":2,"current":0.027,"voltage":242.8,"act_power":0.0,"aprt_power":6.6,"pf":0.00,"freq":50.0,"calibration":"factory"},"em1data:0":{"id":0,"total_act_energy":3458.24,"total_act_ret_energy":1605.24},"em1data:1":{"id":1,"total_act_energy":2768.67,"total_act_ret_energy":25.49},"em1data:2":{"id":2,"total_act_energy":3.09,"total_act_ret_energy":0.71},"eth":{"ip":null},"modbus":{},"mqtt":{"connected":false},"sys":{"mac":"FCE8C0DBA850","restart_required":false,"time":"19:46","unixtime":1731404780,"uptime":563,"ram_size":247148,"ram_free":110596,"fs_size":524288,"fs_free":176128,"cfg_rev":21,"kvs_rev":0,"schedule_rev":3,"webhook_rev":1,"available_updates":{},"reset_reason":3},"temperature:0":{"id": 0,"tC":39.0, "tF":102.2},"wifi":{"sta_ip":"192.168.40.174","status":"got ip","ssid":"IoT","rssi":-67},"ws":{"connected":false}}` require.NoError(t, json.Unmarshal([]byte(jsonstr), &res)) @@ -93,6 +93,26 @@ func TestUnmarshalGen2StatusResponse(t *testing.T) { assert.Equal(t, 3.09, res.Em2Data.TotalActEnergy) assert.Equal(t, 0.71, res.Em2Data.TotalActRetEnergy) } + + { + // Shelly Pro EM-50 channel 0 + 1 + var res Gen2StatusResponse + + jsonstr := `{"ble":{},"bthome":{"errors":["bluetooth_disabled"]},"cloud":{"connected":true},"em1:0":{"id":0,"current":1.473,"voltage":226.9,"act_power":-332.2,"aprt_power":335.0,"pf":0.99,"freq":50.0,"calibration":"factory"},"em1:1":{"id":1,"current":0.428,"voltage":227.0,"act_power":-38.5,"aprt_power":97.4,"pf":0.38,"freq":50.0,"calibration":"factory"},"em1data:0":{"id":0,"total_act_energy":1264.15,"total_act_ret_energy":144792.28},"em1data:1":{"id":1,"total_act_energy":48002.83,"total_act_ret_energy":33241.59},"eth":{"ip":null},"modbus":{},"mqtt":{"connected":false},"switch:0":{"id":0, "source":"HTTP_in", "output":false,"temperature":{"tC":46.4, "tF":115.5}},"sys":{"mac":"08F9E0E8AF2C","restart_required":false,"time":"10:42","unixtime":1742809323,"uptime":3671372,"ram_size":249680,"ram_free":107492,"fs_size":524288,"fs_free":188416,"cfg_rev":12,"kvs_rev":0,"schedule_rev":1,"webhook_rev":0,"available_updates":{"beta":{"version":"1.5.1-beta2"}},"reset_reason":3},"wifi":{"sta_ip":"192.168.1.120","status":"got ip","ssid":"Spaetzlewerk","rssi":-61},"ws":{"connected":false}}` + require.NoError(t, json.Unmarshal([]byte(jsonstr), &res)) + // Channel 0 (1) + assert.Equal(t, -332.2, res.Em0.ActPower) + assert.Equal(t, 1.473, res.Em0.Current) + assert.Equal(t, 226.9, res.Em0.Voltage) + assert.Equal(t, 1264.15, res.Em0Data.TotalActEnergy) + assert.Equal(t, 144792.28, res.Em0Data.TotalActRetEnergy) + // Channel 1 (2) + assert.Equal(t, -38.5, res.Em1.ActPower) + assert.Equal(t, 0.428, res.Em1.Current) + assert.Equal(t, 227.0, res.Em1.Voltage) + assert.Equal(t, 48002.83, res.Em1Data.TotalActEnergy) + assert.Equal(t, 33241.59, res.Em1Data.TotalActRetEnergy) + } } // Test Shelly device info