Metrics: distinguish missing meter energy from zero reading (#30297)

This commit is contained in:
andig 2026-05-31 11:48:43 +02:00 • committed by GitHub
parent 545d239495
commit e3a79f4618
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 28 additions and 39 deletions

View file

@ -496,10 +496,18 @@ func (site *Site) collectMeters(key string, meters []config.Device[api.Meter]) [
fun := func(i int, dev config.Device[api.Meter]) {
meter := dev.Instance()
props := deviceProperties(dev)
mm[i] = types.Measurement{
Name: dev.Config().Name,
Title: props.Title,
Icon: props.Icon,
}
// power
var b bytes.Buffer
power, err := backoff.RetryWithData(meter.CurrentPower, modbus.Backoff())
if err == nil {
mm[i].Power = power
site.log.DEBUG.Printf("%s %d power: %.0fW", key, i+1, power)
} else {
if b.Len() > 0 {
@ -509,22 +517,13 @@ func (site *Site) collectMeters(key string, meters []config.Device[api.Meter]) [
}
// energy (production)
var energy float64
if m, ok := api.Cap[api.MeterEnergy](meter); err == nil && ok {
energy, err = m.TotalEnergy()
if err != nil {
if f, err := m.TotalEnergy(); err == nil {
mm[i].Energy = &f
} else {
site.log.ERROR.Printf("%s %d energy: %v", key, i+1, err)
}
}
props := deviceProperties(dev)
mm[i] = types.Measurement{
Name: dev.Config().Name,
Title: props.Title,
Icon: props.Icon,
Power: power,
Energy: energy,
}
}
var wg sync.WaitGroup
@ -570,7 +569,10 @@ func (site *Site) updatePvMeters() {
return math.Abs(m.ExcessDCPower)
})
totalEnergy := lo.SumBy(mm, func(m types.Measurement) float64 {
return m.Energy
if m.Energy == nil {
return 0
}
return *m.Energy
})
if len(site.pvMeters) > 1 {
@ -589,13 +591,7 @@ func (site *Site) updatePvMeters() {
// persist per-meter PV energy slots (used for history and forecast scaling)
for i, dev := range site.pvMeters {
c := site.collectors[dev.Config().Name]
var importEnergy *float64
if mm[i].Energy > 0 {
importEnergy = &mm[i].Energy
}
if err := c.AddEnergy(importEnergy, nil, mm[i].Power); err != nil {
if err := c.AddEnergy(mm[i].Energy, nil, mm[i].Power); err != nil {
site.log.ERROR.Printf("persist pv %d energy: %v", i+1, err)
}
}
@ -652,7 +648,10 @@ func (site *Site) updateBatteryMeters() {
return m.Power
})
site.battery.Energy = lo.SumBy(mm, func(m types.Measurement) float64 {
return m.Energy
if m.Energy == nil {
return 0
}
return *m.Energy
})
if len(site.batteryMeters) > 1 {
@ -669,11 +668,7 @@ func (site *Site) updateBatteryMeters() {
if !ok {
continue
}
var exportEnergy *float64
if mm[i].Energy > 0 {
exportEnergy = &mm[i].Energy
}
if err := c.AddEnergy(nil, exportEnergy, -mm[i].Power); err != nil {
if err := c.AddEnergy(nil, mm[i].Energy, -mm[i].Power); err != nil {
site.log.ERROR.Printf("persist battery %d energy: %v", i+1, err)
}
}
@ -708,11 +703,7 @@ func (site *Site) addMeterEnergy(meters []config.Device[api.Meter], mm []types.M
if !ok {
continue
}
var importEnergy *float64
if mm[i].Energy > 0 {
importEnergy = &mm[i].Energy
}
if err := c.AddEnergy(importEnergy, nil, mm[i].Power); err != nil {
if err := c.AddEnergy(mm[i].Energy, nil, mm[i].Power); err != nil {
site.log.ERROR.Printf("persist meter %s energy: %v", ref, err)
}
}
@ -790,18 +781,16 @@ func (site *Site) updateGridMeter() error {
}
}
// grid energy (import)
var importEnergy *float64
// grid energy (import); nil when the device has no MeterEnergy capability or the read fails
if energyMeter, ok := api.Cap[api.MeterEnergy](site.gridMeter); ok {
if f, err := energyMeter.TotalEnergy(); err == nil {
mm.Energy = f
importEnergy = &f
mm.Energy = &f
} else {
site.log.ERROR.Printf("grid energy: %v", err)
}
}
site.collectors[site.Meters.GridMeterRef].AddEnergy(importEnergy, nil, mm.Power)
site.collectors[site.Meters.GridMeterRef].AddEnergy(mm.Energy, nil, mm.Power)
site.publish(keys.Grid, mm)

View file

@ -12,8 +12,8 @@ type Measurement struct {
Title string `json:"title,omitempty"`
Icon string `json:"icon,omitempty"`
Power float64 `json:"power"`
Energy float64 `json:"energy,omitempty"`
ReturnEnergy float64 `json:"returnEnergy,omitempty"`
Energy *float64 `json:"energy,omitempty"`
ReturnEnergy *float64 `json:"returnEnergy,omitempty"`
Powers []float64 `json:"powers,omitempty"`
Currents []float64 `json:"currents,omitempty"`
ExcessDCPower float64 `json:"excessdcpower,omitempty"`

View file

@ -115,7 +115,7 @@ func (suite *mqttSuite) TestMeasurement() {
suite.Equal(topics, suite.topics, "topics")
suite.Equal([]string{"", "", "", "0", "", "", "", "", "", "", "", ""}, suite.payloads, "empty payloads")
suite.publish("test", false, types.Measurement{Energy: 1})
suite.publish("test", false, types.Measurement{Energy: new(1.0)})
suite.Equal(topics, suite.topics, "topics")
suite.Equal([]string{"", "", "", "0", "1", "", "", "", "", "", "", ""}, suite.payloads, "energy payloads")