Shelly: fix PM1 Gen3 energy readings (#31957)
This commit is contained in:
parent
7b159d614c
commit
f937ebec1f
5 changed files with 66 additions and 8 deletions
|
|
@ -48,8 +48,13 @@ func NewShellyFromConfig(other map[string]any) (api.Meter, error) {
|
|||
// Three-phase Shelly energy meters count each phase separately (non-balanced),
|
||||
// making their totals unsuitable for bidirectional grid metering.
|
||||
if !(c.usage == "grid" && c.conn.IsThreePhase()) {
|
||||
implement.Has(c, implement.MeterEnergy(c.conn.TotalEnergy))
|
||||
implement.Has(c, implement.MeterReturnEnergy(c.conn.ReturnEnergy))
|
||||
total, ret := c.conn.TotalEnergy, c.conn.ReturnEnergy
|
||||
if c.usage == "pv" {
|
||||
// reverse direction
|
||||
total, ret = ret, total
|
||||
}
|
||||
implement.Has(c, implement.MeterEnergy(total))
|
||||
implement.Has(c, implement.MeterReturnEnergy(ret))
|
||||
}
|
||||
|
||||
if phases, ok := c.conn.Generation.(shelly.Phases); ok {
|
||||
|
|
@ -83,8 +88,16 @@ func (c *Shelly) CurrentPower() (float64, error) {
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if c.usage == "pv" {
|
||||
power = math.Abs(power)
|
||||
}
|
||||
return power, nil
|
||||
return c.currentPowerForUsage(power, c.conn.SignedPower()), nil
|
||||
}
|
||||
|
||||
// PV usage inverts directional power, otherwise the magnitude is used.
|
||||
func (c *Shelly) currentPowerForUsage(power float64, signed bool) float64 {
|
||||
if c.usage != "pv" {
|
||||
return power
|
||||
}
|
||||
if signed {
|
||||
return -power
|
||||
}
|
||||
return math.Abs(power)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,12 @@ type Phases interface {
|
|||
// Connection is the Shelly connection
|
||||
type Connection struct {
|
||||
Generation
|
||||
gen int
|
||||
}
|
||||
|
||||
// SignedPower reports whether the device returns directional (signed) power.
|
||||
func (c *Connection) SignedPower() bool {
|
||||
return c.gen >= 3
|
||||
}
|
||||
|
||||
// NewConnection creates a new Shelly device connection.
|
||||
|
|
@ -79,7 +85,7 @@ func NewConnection(uri, user, password string, channel int, cache time.Duration)
|
|||
}
|
||||
}
|
||||
|
||||
conn := &Connection{gen}
|
||||
conn := &Connection{Generation: gen, gen: resp.Gen}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,7 +232,10 @@ func (c *gen2) TotalEnergy() (float64, error) {
|
|||
|
||||
case c.hasSwitchEndpoint():
|
||||
res, err := c.switchstatus.Get()
|
||||
return res.Aenergy.Total / 1000, err
|
||||
// https://shelly-api-docs.shelly.cloud/gen2/ComponentsAndServices/Switch#status
|
||||
// NOTE: ret_aenergy - the active energy added to this container is also added to aenergy container.
|
||||
// All the consumed energy is collected in aenergy regardless of the direction(consumed or returned) of the active energy.
|
||||
return max(0, res.Aenergy.Total-res.Ret_Aenergy.Total) / 1000, err
|
||||
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown shelly model: %s", c.model)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSignedPower(t *testing.T) {
|
||||
assert.False(t, (&Connection{gen: 1}).SignedPower())
|
||||
assert.False(t, (&Connection{gen: 2}).SignedPower())
|
||||
assert.True(t, (&Connection{gen: 3}).SignedPower())
|
||||
}
|
||||
|
||||
// Test Gen2+ status responses
|
||||
func TestUnmarshalGen2StatusResponse(t *testing.T) {
|
||||
{
|
||||
|
|
|
|||
30
meter/shelly_test.go
Normal file
30
meter/shelly_test.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestShellyCurrentPowerForUsage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
usage string
|
||||
signed bool
|
||||
power float64
|
||||
want float64
|
||||
}{
|
||||
{name: "grid keeps sign", usage: "grid", power: -350, want: -350},
|
||||
{name: "unsigned pv uses absolute value", usage: "pv", power: -350, want: 350},
|
||||
{name: "unsigned pv keeps positive values", usage: "pv", power: 350, want: 350},
|
||||
{name: "signed pv inverts positive values", usage: "pv", signed: true, power: 350, want: -350},
|
||||
{name: "signed pv inverts negative values", usage: "pv", signed: true, power: -350, want: 350},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
m := &Shelly{usage: tc.usage}
|
||||
assert.Equal(t, tc.want, m.currentPowerForUsage(tc.power, tc.signed))
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue