Skip to content
Open
21 changes: 10 additions & 11 deletions core/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ type Site struct {
// meters
circuit api.Circuit // Circuit
hems api.HEMS // HEMS (set by configureHEMS at boot)
gridMeter api.Meter // Grid usage meter
gridMeter config.Device[api.Meter] // Grid usage meter
pvMeters []config.Device[api.Meter] // PV generation meters
batteryMeters []config.Device[api.Meter] // Battery charging meters
extMeters []config.Device[api.Meter] // External meters - for monitoring only
Expand Down Expand Up @@ -206,10 +206,7 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif
return err
}

site.gridMeter = dev.Instance()
if site.gridMeter == nil {
return errors.New("missing grid meter instance")
}
site.gridMeter = dev

me, err := metrics.NewCollector(metrics.Grid, site.Meters.GridMeterRef, metrics.Grid)
if err != nil {
Expand Down Expand Up @@ -456,7 +453,7 @@ func (site *Site) DumpConfig() {
)

if site.gridMeter != nil {
site.log.INFO.Println(meterCapabilities("grid", site.gridMeter))
site.log.INFO.Println(meterCapabilities("grid", site.gridMeter.Instance()))
}

if len(site.pvMeters) > 0 {
Expand Down Expand Up @@ -864,7 +861,9 @@ func (site *Site) updateGridMeter() error {

mm := types.Measurement{Name: site.Meters.GridMeterRef}

if res, err := backoff.RetryWithData(site.gridMeter.CurrentPower, modbus.Backoff()); err == nil {
meter := site.gridMeter.Instance()

if res, err := backoff.RetryWithData(meter.CurrentPower, modbus.Backoff()); err == nil {
Comment thread
andig marked this conversation as resolved.
mm.Power = res
site.gridPower = res
site.log.DEBUG.Printf("grid power: %.0fW", res)
Expand All @@ -873,10 +872,10 @@ func (site *Site) updateGridMeter() error {
}

// grid phase currents (signed)
if phaseMeter, ok := api.Cap[api.PhaseCurrents](site.gridMeter); ok {
if phaseMeter, ok := api.Cap[api.PhaseCurrents](meter); ok {
// grid phase powers
var p1, p2, p3 float64
if phaseMeter, ok := api.Cap[api.PhasePowers](site.gridMeter); ok {
if phaseMeter, ok := api.Cap[api.PhasePowers](meter); ok {
var err error // phases needed for signed currents
if p1, p2, p3, err = phaseMeter.Powers(); err == nil {
mm.Powers = []float64{p1, p2, p3}
Expand All @@ -896,7 +895,7 @@ func (site *Site) updateGridMeter() error {

// grid energy (import); nil when the device has no MeterEnergy capability or the read fails
// ignore spurious zero readings (NaN-derived or nightly reset, #30950)
if energyMeter, ok := api.Cap[api.MeterEnergy](site.gridMeter); ok {
if energyMeter, ok := api.Cap[api.MeterEnergy](meter); ok {
if f, err := nonZeroEnergy(energyMeter.TotalEnergy()); err == nil {
mm.Energy = &f
} else if !errors.Is(err, api.ErrNotAvailable) {
Expand All @@ -906,7 +905,7 @@ func (site *Site) updateGridMeter() error {

// grid return energy (export); nil when the device has no MeterReturnEnergy capability or the read fails
// ignore spurious zero readings as above
if returnEnergyMeter, ok := api.Cap[api.MeterReturnEnergy](site.gridMeter); ok {
if returnEnergyMeter, ok := api.Cap[api.MeterReturnEnergy](meter); ok {
if f, err := nonZeroEnergy(returnEnergyMeter.ReturnEnergy()); err == nil {
mm.ReturnEnergy = &f
} else if !errors.Is(err, api.ErrNotAvailable) {
Expand Down
9 changes: 7 additions & 2 deletions core/site_circuits.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (site *Site) dimMeters(dim bool) error {
return errs
}

// curtailPV applies the HEMS curtailment percent to all curtailable pv meters.
// curtailPV applies the HEMS curtailment percent to all curtailable pv or grid meters.
// Devices are only queried when the percent changes or after a failed attempt.
func (site *Site) curtailPV(percent *int) error {
if percent == nil || site.curtailPercent != nil && *site.curtailPercent == *percent {
Expand All @@ -107,8 +107,13 @@ func (site *Site) curtailPV(percent *int) error {
// invalidate until successfully applied
site.curtailPercent = nil

meters := slices.Clone(site.pvMeters)
if site.gridMeter != nil {
meters = append(meters, site.gridMeter)
}

var errs error
for _, dev := range site.pvMeters {
for _, dev := range meters {
Comment thread
andig marked this conversation as resolved.
m, ok := api.Cap[api.Curtailer](dev.Instance())
if !ok {
continue
Expand Down
60 changes: 60 additions & 0 deletions templates/definition/meter/sma-homemanager-modbus.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
template: sma-homemanager-modbus
products:
- brand: SMA
description:
generic: Sunny Home Manager 2.0 Modbus
capabilities: ["curtail"]
params:
- name: usage
choice: ["grid"]
- name: modbus
choice: ["tcpip"]
port: 502
id: 2
help:
en: ModbusTCP server needs to be enabled.
de: Der ModbusTCP Server muss aktiviert sein.
render: |
type: custom
power:
source: calc
add:
- source: modbus
{{- include "modbus" . | indent 6 }}
register:
address: 30865 # SMA Modbus Profile: Metering.GridMs.TotWIn
type: input
decode: int32nan
- source: modbus
{{- include "modbus" . | indent 6 }}
register:
address: 30867 # SMA Modbus Profile: Metering.GridMs.TotWOut
type: input
decode: int32nan
scale: -1
energy:
source: modbus
{{- include "modbus" . | indent 2 }}
register:
address: 30581 # SMA Modbus Profile: Metering.GridMs.TotWhIn
type: holding
decode: int32nan
scale: 0.001
returnenergy:
source: modbus
{{- include "modbus" . | indent 2 }}
register:
address: 30583 # SMA Modbus Profile: Metering.GridMs.TotWhOut
type: holding
decode: int32nan
scale: 0.001
curtail:
source: modbus
{{- include "modbus" . | indent 2 }}
register:
address: 40016 # Wirkleistungssollwert P, in % der maximalen Wirkleistung (PMAX) der PV-Anlage.
type: writesingle
decode: uint16
curtailed:
source: error
error: ErrNotAvailable
Loading