Repository: evcc-io/evcc
Pull request #19301 (2025-03-01 17:57:12)

Modbus: use context
Fix https://github.com/evcc-io/evcc/issues/19293

@naltatis könntest Du das mit Deinem Sungrow mal testen? ~~Ich muss aber erst die Zähler fertig machen...~~


Patch:
diff --git a/charger/abb.go b/charger/abb.go
index 3fb47ce7..526207db 100644
--- a/charger/abb.go
+++ b/charger/abb.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"time"
@@ -52,11 +53,11 @@ const (
 )
 
 func init() {
-	registry.Add("abb", NewABBFromConfig)
+	registry.AddCtx("abb", NewABBFromConfig)
 }
 
 // NewABBFromConfig creates a ABB charger from generic config
-func NewABBFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewABBFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.Settings{
 		ID: 1,
 	}
@@ -65,12 +66,12 @@ func NewABBFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewABB(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
+	return NewABB(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
 }
 
 // NewABB creates ABB charger
-func NewABB(uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
+func NewABB(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/abl-em4.go b/charger/abl-em4.go
index ed282176..441d4612 100644
--- a/charger/abl-em4.go
+++ b/charger/abl-em4.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 
@@ -48,11 +49,11 @@ type AblEm4 struct {
 }
 
 func init() {
-	registry.Add("abl-em4", NewAblEm4FromConfig)
+	registry.AddCtx("abl-em4", NewAblEm4FromConfig)
 }
 
 // NewAblEm4FromConfig creates an ABL eM4 charger from generic config
-func NewAblEm4FromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewAblEm4FromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := struct {
 		modbus.TcpSettings `mapstructure:",squash"`
 		Connector          uint16
@@ -67,12 +68,12 @@ func NewAblEm4FromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewAblEm4(cc.URI, cc.ID, cc.Connector)
+	return NewAblEm4(ctx, cc.URI, cc.ID, cc.Connector)
 }
 
 // NewAblEm4 creates an ABL eM4 charger
-func NewAblEm4(uri string, id uint8, connector uint16) (*AblEm4, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+func NewAblEm4(ctx context.Context, uri string, id uint8, connector uint16) (*AblEm4, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/abl.go b/charger/abl.go
index 40263284..3656d9f0 100644
--- a/charger/abl.go
+++ b/charger/abl.go
@@ -19,6 +19,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"time"
@@ -73,13 +74,13 @@ var ablStatus = map[byte]string{
 }
 
 func init() {
-	registry.Add("abl", NewABLeMHFromConfig)
+	registry.AddCtx("abl", NewABLeMHFromConfig)
 }
 
 // https://www.goingelectric.de/forum/viewtopic.php?p=1550459#p1550459
 
 // NewABLeMHFromConfig creates a ABLeMH charger from generic config
-func NewABLeMHFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewABLeMHFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := struct {
 		modbus.Settings `mapstructure:",squash"`
 		Timeout         time.Duration
@@ -93,14 +94,14 @@ func NewABLeMHFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewABLeMH(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID, cc.Timeout)
+	return NewABLeMH(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID, cc.Timeout)
 }
 
 //go:generate go tool decorate -f decorateABLeMH -b *ABLeMH -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)"
 
 // NewABLeMH creates ABLeMH charger
-func NewABLeMH(uri, device, comset string, baudrate int, slaveID uint8, timeout time.Duration) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, modbus.Ascii, slaveID)
+func NewABLeMH(ctx context.Context, uri, device, comset string, baudrate int, slaveID uint8, timeout time.Duration) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, modbus.Ascii, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/alfen.go b/charger/alfen.go
index b1a2965d..d0f2c289 100644
--- a/charger/alfen.go
+++ b/charger/alfen.go
@@ -73,7 +73,7 @@ func NewAlfenFromConfig(ctx context.Context, other map[string]interface{}) (api.
 
 // NewAlfen creates Alfen charger
 func NewAlfen(ctx context.Context, uri string, slaveID uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/alphatec.go b/charger/alphatec.go
index 7e76df63..25b826e5 100644
--- a/charger/alphatec.go
+++ b/charger/alphatec.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"time"
@@ -42,11 +43,11 @@ const (
 )
 
 func init() {
-	registry.Add("alphatec", NewAlphatecFromConfig)
+	registry.AddCtx("alphatec", NewAlphatecFromConfig)
 }
 
 // NewAlphatecFromConfig creates a Alphatec charger from generic config
-func NewAlphatecFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewAlphatecFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.Settings{
 		ID: 1,
 	}
@@ -55,12 +56,12 @@ func NewAlphatecFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewAlphatec(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
+	return NewAlphatec(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
 }
 
 // NewAlphatec creates Alphatec charger
-func NewAlphatec(uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
+func NewAlphatec(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/amperfied.go b/charger/amperfied.go
index ba0d9010..80115ece 100644
--- a/charger/amperfied.go
+++ b/charger/amperfied.go
@@ -80,7 +80,7 @@ func NewAmperfiedFromConfig(ctx context.Context, other map[string]interface{}) (
 
 // NewAmperfied creates Amperfied charger
 func NewAmperfied(ctx context.Context, uri string, slaveID uint8, phases bool) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/bender.go b/charger/bender.go
index 917a509d..bf959698 100644
--- a/charger/bender.go
+++ b/charger/bender.go
@@ -24,6 +24,7 @@ package charger
 // * Set 'Allow UID Disclose' to On
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"math"
@@ -68,11 +69,11 @@ const (
 )
 
 func init() {
-	registry.Add("bender", NewBenderCCFromConfig)
+	registry.AddCtx("bender", NewBenderCCFromConfig)
 }
 
 // NewBenderCCFromConfig creates a BenderCC charger from generic config
-func NewBenderCCFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewBenderCCFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: 255,
 	}
@@ -81,14 +82,14 @@ func NewBenderCCFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewBenderCC(cc.URI, cc.ID)
+	return NewBenderCC(ctx, cc.URI, cc.ID)
 }
 
 //go:generate go tool decorate -f decorateBenderCC -b *BenderCC -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.Identifier,Identify,func() (string, error)"
 
 // NewBenderCC creates BenderCC charger
-func NewBenderCC(uri string, id uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+func NewBenderCC(ctx context.Context, uri string, id uint8) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/cfos.go b/charger/cfos.go
index e8100b07..62199b65 100644
--- a/charger/cfos.go
+++ b/charger/cfos.go
@@ -1,6 +1,7 @@
 package charger
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 
@@ -34,13 +35,13 @@ type CfosPowerBrain struct {
 }
 
 func init() {
-	registry.Add("cfos", NewCfosPowerBrainFromConfig)
+	registry.AddCtx("cfos", NewCfosPowerBrainFromConfig)
 }
 
 //go:generate go tool decorate -f decorateCfos -b *CfosPowerBrain -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseSwitcher,Phases1p3p,func(int) error"
 
 // NewCfosPowerBrainFromConfig creates a cFos charger from generic config
-func NewCfosPowerBrainFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewCfosPowerBrainFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: 1,
 	}
@@ -49,14 +50,14 @@ func NewCfosPowerBrainFromConfig(other map[string]interface{}) (api.Charger, err
 		return nil, err
 	}
 
-	return NewCfosPowerBrain(cc.URI, cc.ID)
+	return NewCfosPowerBrain(ctx, cc.URI, cc.ID)
 }
 
 // NewCfosPowerBrain creates a cFos charger
-func NewCfosPowerBrain(uri string, id uint8) (api.Charger, error) {
+func NewCfosPowerBrain(ctx context.Context, uri string, id uint8) (api.Charger, error) {
 	uri = util.DefaultPort(uri, 4701)
 
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/dadapower.go b/charger/dadapower.go
index 71abe6b0..3cb8bc90 100644
--- a/charger/dadapower.go
+++ b/charger/dadapower.go
@@ -53,7 +53,7 @@ func NewDadapowerFromConfig(ctx context.Context, other map[string]interface{}) (
 
 // NewDadapower creates a Dadapower charger
 func NewDadapower(ctx context.Context, uri string, id uint8) (*Dadapower, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/daheimladen-mb.go b/charger/daheimladen-mb.go
index 15e50937..e70d70cb 100644
--- a/charger/daheimladen-mb.go
+++ b/charger/daheimladen-mb.go
@@ -75,7 +75,7 @@ func NewDaheimLadenMBFromConfig(ctx context.Context, other map[string]interface{
 
 // NewDaheimLadenMB creates DaheimLadenMB charger
 func NewDaheimLadenMB(ctx context.Context, uri string, id uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/delta.go b/charger/delta.go
index e1cd3807..2ebfcc15 100644
--- a/charger/delta.go
+++ b/charger/delta.go
@@ -84,7 +84,7 @@ func NewDeltaFromConfig(ctx context.Context, other map[string]interface{}) (api.
 
 // NewDelta creates Delta charger
 func NewDelta(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8, connector uint16) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/em2go.go b/charger/em2go.go
index 49abf176..21bcabe9 100644
--- a/charger/em2go.go
+++ b/charger/em2go.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"time"
@@ -62,14 +63,14 @@ const (
 )
 
 func init() {
-	registry.Add("em2go", NewEm2GoFromConfig)
-	registry.Add("em2go-home", NewEm2GoFromConfig)
+	registry.AddCtx("em2go", NewEm2GoFromConfig)
+	registry.AddCtx("em2go-home", NewEm2GoFromConfig)
 }
 
 //go:generate go tool decorate -f decorateEm2Go -b *Em2Go -r api.Charger -t "api.ChargerEx,MaxCurrentMillis,func(float64) error" -t "api.PhaseSwitcher,Phases1p3p,func(int) error" -t "api.PhaseGetter,GetPhases,func() (int, error)"
 
 // NewEm2GoFromConfig creates a Em2Go charger from generic config
-func NewEm2GoFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewEm2GoFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: 255,
 	}
@@ -78,14 +79,14 @@ func NewEm2GoFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewEm2Go(cc.URI, cc.ID)
+	return NewEm2Go(ctx, cc.URI, cc.ID)
 }
 
 // NewEm2Go creates Em2Go charger
-func NewEm2Go(uri string, slaveID uint8) (api.Charger, error) {
+func NewEm2Go(ctx context.Context, uri string, slaveID uint8) (api.Charger, error) {
 	uri = util.DefaultPort(uri, 502)
 
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/etrel.go b/charger/etrel.go
index 2fa84cc4..ffd39af3 100644
--- a/charger/etrel.go
+++ b/charger/etrel.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"math"
@@ -59,11 +60,11 @@ type Etrel struct {
 }
 
 func init() {
-	registry.Add("etrel", NewEtrelFromConfig)
+	registry.AddCtx("etrel", NewEtrelFromConfig)
 }
 
 // NewEtrelFromConfig creates a Etrel charger from generic config
-func NewEtrelFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewEtrelFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := struct {
 		Connector          int
 		modbus.TcpSettings `mapstructure:",squash"`
@@ -78,12 +79,12 @@ func NewEtrelFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewEtrel(cc.Connector, cc.URI, cc.ID)
+	return NewEtrel(ctx, cc.URI, cc.ID, cc.Connector)
 }
 
 // NewEtrel creates a Etrel charger
-func NewEtrel(connector int, uri string, id uint8) (*Etrel, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+func NewEtrel(ctx context.Context, uri string, id uint8, connector int) (*Etrel, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/evsedin.go b/charger/evsedin.go
index fb7820d4..c891ce66 100644
--- a/charger/evsedin.go
+++ b/charger/evsedin.go
@@ -1,6 +1,7 @@
 package charger
 
 import (
+	"context"
 	"fmt"
 	"time"
 
@@ -21,14 +22,14 @@ const (
 )
 
 func init() {
-	registry.Add("simpleevse", NewEvseDINFromConfig) // deprecated
-	registry.Add("evsedin", NewEvseDINFromConfig)
+	registry.AddCtx("simpleevse", NewEvseDINFromConfig) // deprecated
+	registry.AddCtx("evsedin", NewEvseDINFromConfig)
 }
 
 // https://files.ev-power.eu/inc/_doc/attach/StoItem/4418/evse-wb-din_Manual.pdf
 
 // NewEvseDINFromConfig creates an EVSE DIN charger from generic config
-func NewEvseDINFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewEvseDINFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.Settings{
 		Baudrate: 9600,
 		Comset:   "8N1",
@@ -39,14 +40,14 @@ func NewEvseDINFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewEvseDIN(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
+	return NewEvseDIN(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
 }
 
 // NewEvseDIN creates EVSE DIN charger
-func NewEvseDIN(uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
+func NewEvseDIN(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
 	log := util.NewLogger("evse")
 
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/heidelberg-ec.go b/charger/heidelberg-ec.go
index b5675897..530bdf31 100644
--- a/charger/heidelberg-ec.go
+++ b/charger/heidelberg-ec.go
@@ -76,7 +76,7 @@ func NewHeidelbergECFromConfig(ctx context.Context, other map[string]interface{}
 
 // NewHeidelbergEC creates HeidelbergEC charger
 func NewHeidelbergEC(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/hesotec.go b/charger/hesotec.go
index 9a6b913e..d8c5d00b 100644
--- a/charger/hesotec.go
+++ b/charger/hesotec.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"time"
@@ -55,11 +56,11 @@ const (
 )
 
 func init() {
-	registry.Add("hesotec", NewHesotecFromConfig)
+	registry.AddCtx("hesotec", NewHesotecFromConfig)
 }
 
 // NewHesotecFromConfig creates a Hesotec charger from generic config
-func NewHesotecFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewHesotecFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: 1,
 	}
@@ -68,12 +69,12 @@ func NewHesotecFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewHesotec(cc.URI, cc.ID)
+	return NewHesotec(ctx, cc.URI, cc.ID)
 }
 
 // NewHesotec creates Hesotec charger
-func NewHesotec(uri string, id uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+func NewHesotec(ctx context.Context, uri string, id uint8) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/innogy.go b/charger/innogy.go
index 1f66c95d..fd3f0a18 100644
--- a/charger/innogy.go
+++ b/charger/innogy.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"math"
@@ -51,11 +52,11 @@ type Innogy struct {
 }
 
 func init() {
-	registry.Add("innogy", NewInnogyFromConfig)
+	registry.AddCtx("innogy", NewInnogyFromConfig)
 }
 
 // NewInnogyFromConfig creates a Innogy charger from generic config
-func NewInnogyFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewInnogyFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: 1,
 	}
@@ -64,7 +65,7 @@ func NewInnogyFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	wb, err := NewInnogy(cc.URI, cc.ID)
+	wb, err := NewInnogy(ctx, cc.URI, cc.ID)
 	if err != nil {
 		return nil, err
 	}
@@ -85,8 +86,8 @@ func NewInnogyFromConfig(other map[string]interface{}) (api.Charger, error) {
 //go:generate go tool decorate -f decorateInnogy -b *Innogy -r api.Charger -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)"
 
 // NewInnogy creates a Innogy charger
-func NewInnogy(uri string, id uint8) (*Innogy, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+func NewInnogy(ctx context.Context, uri string, id uint8) (*Innogy, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/keba-modbus.go b/charger/keba-modbus.go
index a7d33fc0..6ad7d20c 100644
--- a/charger/keba-modbus.go
+++ b/charger/keba-modbus.go
@@ -139,7 +139,7 @@ func NewKebaFromConfig(ctx context.Context, other map[string]interface{}) (api.C
 
 // NewKeba creates a new charger
 func NewKeba(ctx context.Context, embed embed, uri string, slaveID uint8) (*Keba, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/kse.go b/charger/kse.go
index b44c1ecc..afcd8885 100644
--- a/charger/kse.go
+++ b/charger/kse.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 
@@ -52,11 +53,11 @@ const (
 )
 
 func init() {
-	registry.Add("kse", NewKSEFromConfig)
+	registry.AddCtx("kse", NewKSEFromConfig)
 }
 
 // NewKSEFromConfig creates a KSE charger from generic config
-func NewKSEFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewKSEFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.Settings{
 		ID:       100,
 		Baudrate: 9600,
@@ -67,14 +68,14 @@ func NewKSEFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewKSE(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID)
+	return NewKSE(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID)
 }
 
 //go:generate go tool decorate -f decorateKSE -b *KSE -r api.Charger -t "api.PhaseSwitcher,Phases1p3p,func(int) error" -t "api.PhaseGetter,GetPhases,func() (int, error)" -t "api.Identifier,Identify,func() (string, error)"
 
 // NewKSE creates KSE charger
-func NewKSE(uri, device, comset string, baudrate int, slaveID uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, modbus.Rtu, slaveID)
+func NewKSE(ctx context.Context, uri, device, comset string, baudrate int, slaveID uint8) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, modbus.Rtu, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/mennekes-compact.go b/charger/mennekes-compact.go
index 8a697a8a..39495f41 100644
--- a/charger/mennekes-compact.go
+++ b/charger/mennekes-compact.go
@@ -91,7 +91,7 @@ func NewMennekesCompactFromConfig(ctx context.Context, other map[string]interfac
 
 // NewMennekesCompact creates Mennekes charger
 func NewMennekesCompact(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8, timeout time.Duration) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/mennekes-hcc3.go b/charger/mennekes-hcc3.go
index 2107fe67..a1ab5f9c 100644
--- a/charger/mennekes-hcc3.go
+++ b/charger/mennekes-hcc3.go
@@ -19,6 +19,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 
@@ -49,11 +50,11 @@ const (
 )
 
 func init() {
-	registry.Add("mennekes-hcc3", NewMennekesHcc3FromConfig)
+	registry.AddCtx("mennekes-hcc3", NewMennekesHcc3FromConfig)
 }
 
 // NewMennekesHcc3FromConfig creates a Mennekes mennekesHcc3 charger from generic config
-func NewMennekesHcc3FromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewMennekesHcc3FromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: 255,
 	}
@@ -62,14 +63,14 @@ func NewMennekesHcc3FromConfig(other map[string]interface{}) (api.Charger, error
 		return nil, err
 	}
 
-	return NewMennekesHcc3(cc.URI, cc.ID)
+	return NewMennekesHcc3(ctx, cc.URI, cc.ID)
 }
 
 // NewMennekesHcc3 creates Mennekes HCC3 charger
-func NewMennekesHcc3(uri string, slaveID uint8) (api.Charger, error) {
+func NewMennekesHcc3(ctx context.Context, uri string, slaveID uint8) (api.Charger, error) {
 	uri = util.DefaultPort(uri, 502)
 
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/mypv.go b/charger/mypv.go
index b0137007..152542db 100644
--- a/charger/mypv.go
+++ b/charger/mypv.go
@@ -84,7 +84,7 @@ func newMyPvFromConfig(ctx context.Context, name string, other map[string]interf
 
 // NewMyPv creates myPV AC Elwa 2 or Thor charger
 func NewMyPv(ctx context.Context, name, uri string, slaveID uint8, tempSource int, statusC uint16) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/nrggen2.go b/charger/nrggen2.go
index 2b4e5003..1254d68a 100644
--- a/charger/nrggen2.go
+++ b/charger/nrggen2.go
@@ -1,6 +1,7 @@
 package charger
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"math"
@@ -45,13 +46,13 @@ const (
 )
 
 func init() {
-	registry.Add("nrggen2", NewNRGKickGen2FromConfig)
+	registry.AddCtx("nrggen2", NewNRGKickGen2FromConfig)
 }
 
 //go:generate go tool decorate -f decorateNRGKickGen2 -b *NRGKickGen2 -r api.Charger -t "api.PhaseSwitcher,Phases1p3p,func(int) error"
 
 // NewNRGKickGen2FromConfig creates a NRGKickGen2 charger from generic config
-func NewNRGKickGen2FromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewNRGKickGen2FromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := struct {
 		modbus.TcpSettings `mapstructure:",squash"`
 		Phases1p3p         bool
@@ -66,7 +67,7 @@ func NewNRGKickGen2FromConfig(other map[string]interface{}) (api.Charger, error)
 		return nil, err
 	}
 
-	nrg, err := NewNRGKickGen2(cc.URI, cc.ID)
+	nrg, err := NewNRGKickGen2(ctx, cc.URI, cc.ID)
 	if err != nil {
 		return nil, err
 	}
@@ -85,8 +86,8 @@ func NewNRGKickGen2FromConfig(other map[string]interface{}) (api.Charger, error)
 }
 
 // NewNRGKickGen2 creates NRGKickGen2 charger
-func NewNRGKickGen2(uri string, slaveID uint8) (*NRGKickGen2, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+func NewNRGKickGen2(ctx context.Context, uri string, slaveID uint8) (*NRGKickGen2, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/obo.go b/charger/obo.go
index 3dc85067..826ffa32 100644
--- a/charger/obo.go
+++ b/charger/obo.go
@@ -45,7 +45,7 @@ func NewOboFromConfig(ctx context.Context, other map[string]interface{}) (api.Ch
 
 // NewObo creates OBO Bettermann charger
 func NewObo(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/openwb-2.0.go b/charger/openwb-2.0.go
index 48540b72..39eb9c80 100644
--- a/charger/openwb-2.0.go
+++ b/charger/openwb-2.0.go
@@ -1,6 +1,7 @@
 package charger
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 
@@ -35,7 +36,7 @@ const (
 )
 
 func init() {
-	registry.Add("openwb-2.0", NewOpenWB20FromConfig)
+	registry.AddCtx("openwb-2.0", NewOpenWB20FromConfig)
 }
 
 // https://openwb.de/main/wp-content/uploads/2023/10/ModbusTCP-openWB-series2-Pro-1.pdf
@@ -43,7 +44,7 @@ func init() {
 //go:generate go tool decorate -f decorateOpenWB20 -b *OpenWB20 -r api.Charger -t "api.PhaseSwitcher,Phases1p3p,func(int) error" -t "api.Identifier,Identify,func() (string, error)"
 
 // NewOpenWB20FromConfig creates a OpenWB20 charger from generic config
-func NewOpenWB20FromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewOpenWB20FromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := struct {
 		Connector          uint16
 		Phases1p3p         bool
@@ -59,7 +60,7 @@ func NewOpenWB20FromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	wb, err := NewOpenWB20(cc.URI, cc.ID, cc.Connector)
+	wb, err := NewOpenWB20(ctx, cc.URI, cc.ID, cc.Connector)
 	if err != nil {
 		return nil, err
 	}
@@ -78,10 +79,10 @@ func NewOpenWB20FromConfig(other map[string]interface{}) (api.Charger, error) {
 }
 
 // NewOpenWB20 creates OpenWB20 charger
-func NewOpenWB20(uri string, slaveID uint8, connector uint16) (*OpenWB20, error) {
+func NewOpenWB20(ctx context.Context, uri string, slaveID uint8, connector uint16) (*OpenWB20, error) {
 	uri = util.DefaultPort(uri, 1502)
 
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/peblar.go b/charger/peblar.go
index 153ee428..4e21bf72 100644
--- a/charger/peblar.go
+++ b/charger/peblar.go
@@ -20,6 +20,7 @@ package charger
 // Details on the Peblar modbus server obtained from: https://developer.peblar.com/modbus-api
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 
@@ -67,13 +68,13 @@ const (
 )
 
 func init() {
-	registry.Add("peblar", NewPeblarFromConfig)
+	registry.AddCtx("peblar", NewPeblarFromConfig)
 }
 
 //go:generate go tool decorate -f decoratePeblar -b *Peblar -r api.Charger -t "api.PhaseSwitcher,Phases1p3p,func(int) error" -t "api.PhaseGetter,GetPhases,func() (int, error)"
 
 // NewPeblarFromConfig creates a Peblar charger from generic config
-func NewPeblarFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewPeblarFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: 255,
 	}
@@ -82,12 +83,12 @@ func NewPeblarFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewPeblar(cc.URI, cc.ID)
+	return NewPeblar(ctx, cc.URI, cc.ID)
 }
 
 // NewPeblar creates Peblar charger
-func NewPeblar(uri string, id uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+func NewPeblar(ctx context.Context, uri string, id uint8) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/phoenix-charx.go b/charger/phoenix-charx.go
index 24be5e11..62a22d37 100644
--- a/charger/phoenix-charx.go
+++ b/charger/phoenix-charx.go
@@ -1,6 +1,7 @@
 package charger
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"time"
@@ -42,13 +43,13 @@ type PhoenixCharx struct {
 }
 
 func init() {
-	registry.Add("phoenix-charx", NewPhoenixCharxFromConfig)
+	registry.AddCtx("phoenix-charx", NewPhoenixCharxFromConfig)
 }
 
 //go:generate go tool decorate -f decoratePhoenixCharx -b *PhoenixCharx -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)"
 
 // NewPhoenixCharxFromConfig creates a Phoenix charger from generic config
-func NewPhoenixCharxFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewPhoenixCharxFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := struct {
 		modbus.TcpSettings `mapstructure:",squash"`
 		Connector          uint16
@@ -63,7 +64,7 @@ func NewPhoenixCharxFromConfig(other map[string]interface{}) (api.Charger, error
 		return nil, err
 	}
 
-	wb, err := NewPhoenixCharx(cc.URI, cc.ID, cc.Connector)
+	wb, err := NewPhoenixCharx(ctx, cc.URI, cc.ID, cc.Connector)
 	if err != nil {
 		return nil, err
 	}
@@ -81,8 +82,8 @@ func NewPhoenixCharxFromConfig(other map[string]interface{}) (api.Charger, error
 }
 
 // NewPhoenixCharx creates a Phoenix charger
-func NewPhoenixCharx(uri string, id uint8, connector uint16) (*PhoenixCharx, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+func NewPhoenixCharx(ctx context.Context, uri string, id uint8, connector uint16) (*PhoenixCharx, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/phoenix-em-eth.go b/charger/phoenix-em-eth.go
index b52babcb..1d5e1aea 100644
--- a/charger/phoenix-em-eth.go
+++ b/charger/phoenix-em-eth.go
@@ -1,6 +1,7 @@
 package charger
 
 import (
+	"context"
 	"fmt"
 	"time"
 
@@ -28,13 +29,13 @@ type PhoenixEMEth struct {
 }
 
 func init() {
-	registry.Add("phoenix-em-eth", NewPhoenixEMEthFromConfig)
+	registry.AddCtx("phoenix-em-eth", NewPhoenixEMEthFromConfig)
 }
 
 //go:generate go tool decorate -f decoratePhoenixEMEth -b *PhoenixEMEth -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)"
 
 // NewPhoenixEMEthFromConfig creates a Phoenix charger from generic config
-func NewPhoenixEMEthFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewPhoenixEMEthFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: 180,
 	}
@@ -43,7 +44,7 @@ func NewPhoenixEMEthFromConfig(other map[string]interface{}) (api.Charger, error
 		return nil, err
 	}
 
-	wb, err := NewPhoenixEMEth(cc.URI, cc.ID)
+	wb, err := NewPhoenixEMEth(ctx, cc.URI, cc.ID)
 	if err != nil {
 		return nil, err
 	}
@@ -67,8 +68,8 @@ func NewPhoenixEMEthFromConfig(other map[string]interface{}) (api.Charger, error
 }
 
 // NewPhoenixEMEth creates a Phoenix charger
-func NewPhoenixEMEth(uri string, slaveID uint8) (*PhoenixEMEth, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+func NewPhoenixEMEth(ctx context.Context, uri string, slaveID uint8) (*PhoenixEMEth, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/phoenix-ev-eth.go b/charger/phoenix-ev-eth.go
index 0f2d7e72..2772322c 100644
--- a/charger/phoenix-ev-eth.go
+++ b/charger/phoenix-ev-eth.go
@@ -24,6 +24,7 @@ package charger
 // * Set DIP switch 10 to ON
 
 import (
+	"context"
 	"fmt"
 
 	"github.com/evcc-io/evcc/api"
@@ -58,13 +59,13 @@ const (
 )
 
 func init() {
-	registry.Add("phoenix-ev-eth", NewPhoenixEVEthFromConfig)
+	registry.AddCtx("phoenix-ev-eth", NewPhoenixEVEthFromConfig)
 }
 
 //go:generate go tool decorate -f decoratePhoenixEVEth -b *PhoenixEVEth -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.ChargerEx,MaxCurrentMillis,func(float64) error" -t "api.Identifier,Identify,func() (string, error)"
 
 // NewPhoenixEVEthFromConfig creates a PhoenixEVEth charger from generic config
-func NewPhoenixEVEthFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewPhoenixEVEthFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: 255,
 	}
@@ -73,12 +74,12 @@ func NewPhoenixEVEthFromConfig(other map[string]interface{}) (api.Charger, error
 		return nil, err
 	}
 
-	return NewPhoenixEVEth(cc.URI, cc.ID)
+	return NewPhoenixEVEth(ctx, cc.URI, cc.ID)
 }
 
 // NewPhoenixEVEth creates a PhoenixEVEth charger
-func NewPhoenixEVEth(uri string, slaveID uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+func NewPhoenixEVEth(ctx context.Context, uri string, slaveID uint8) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/phoenix-ev-ser.go b/charger/phoenix-ev-ser.go
index 3e5366d1..6e679907 100644
--- a/charger/phoenix-ev-ser.go
+++ b/charger/phoenix-ev-ser.go
@@ -1,6 +1,7 @@
 package charger
 
 import (
+	"context"
 	"fmt"
 
 	"github.com/evcc-io/evcc/api"
@@ -21,11 +22,11 @@ type PhoenixEVSer struct {
 }
 
 func init() {
-	registry.Add("phoenix-ev-ser", NewPhoenixEVSerFromConfig)
+	registry.AddCtx("phoenix-ev-ser", NewPhoenixEVSerFromConfig)
 }
 
 // NewPhoenixEVSerFromConfig creates a Phoenix charger from generic config
-func NewPhoenixEVSerFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewPhoenixEVSerFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.Settings{
 		ID: 1,
 	}
@@ -34,12 +35,12 @@ func NewPhoenixEVSerFromConfig(other map[string]interface{}) (api.Charger, error
 		return nil, err
 	}
 
-	return NewPhoenixEVSer(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
+	return NewPhoenixEVSer(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
 }
 
 // NewPhoenixEVSer creates a Phoenix charger
-func NewPhoenixEVSer(uri, device, comset string, baudrate int, proto modbus.Protocol, id uint8) (*PhoenixEVSer, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, id)
+func NewPhoenixEVSer(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, id uint8) (*PhoenixEVSer, error) {
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/prachtalpha.go b/charger/prachtalpha.go
index 6bdeb75e..9a3c5046 100644
--- a/charger/prachtalpha.go
+++ b/charger/prachtalpha.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"time"
@@ -46,11 +47,11 @@ const (
 )
 
 func init() {
-	registry.Add("pracht-alpha", NewPrachtAlphaFromConfig)
+	registry.AddCtx("pracht-alpha", NewPrachtAlphaFromConfig)
 }
 
 // NewPrachtAlphaFromConfig creates a PrachtAlpha charger from generic config
-func NewPrachtAlphaFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewPrachtAlphaFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := struct {
 		Connector       uint16
 		modbus.Settings `mapstructure:",squash"`
@@ -66,12 +67,12 @@ func NewPrachtAlphaFromConfig(other map[string]interface{}) (api.Charger, error)
 		return nil, err
 	}
 
-	return NewPrachtAlpha(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID, cc.Timeout, cc.Connector)
+	return NewPrachtAlpha(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID, cc.Timeout, cc.Connector)
 }
 
 // NewPrachtAlpha creates PrachtAlpha charger
-func NewPrachtAlpha(uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8, timeout time.Duration, vehicle uint16) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
+func NewPrachtAlpha(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8, timeout time.Duration, vehicle uint16) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/pulsares.go b/charger/pulsares.go
index 34a0ac77..7277ae51 100644
--- a/charger/pulsares.go
+++ b/charger/pulsares.go
@@ -78,7 +78,7 @@ func NewPulsaresFromConfig(ctx context.Context, other map[string]interface{}) (a
 
 // NewPulsares creates Pulsares charger
 func NewPulsares(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (*Pulsares, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/schneider-v3.go b/charger/schneider-v3.go
index 66c63c3d..b00b269e 100644
--- a/charger/schneider-v3.go
+++ b/charger/schneider-v3.go
@@ -76,7 +76,7 @@ func NewSchneiderV3FromConfig(ctx context.Context, other map[string]interface{})
 
 // NewSchneiderV3 creates Schneider charger
 func NewSchneiderV3(ctx context.Context, uri string, id uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/smartevse.go b/charger/smartevse.go
index 7d1c480b..00079417 100644
--- a/charger/smartevse.go
+++ b/charger/smartevse.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 	"strconv"
@@ -64,11 +65,11 @@ const (
 )
 
 func init() {
-	registry.Add("smartevse", NewsmartEVSEFromConfig)
+	registry.AddCtx("smartevse", NewsmartEVSEFromConfig)
 }
 
 // NewsmartEVSEFromConfig creates a new smartEVSE ModbusTCP charger
-func NewsmartEVSEFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewsmartEVSEFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.Settings{
 		ID: 1,
 	}
@@ -77,12 +78,12 @@ func NewsmartEVSEFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewsmartEVSE(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
+	return NewsmartEVSE(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
 }
 
 // NewsmartEVSE creates a new charger
-func NewsmartEVSE(uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (*smartEVSE, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
+func NewsmartEVSE(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (*smartEVSE, error) {
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/solax.go b/charger/solax.go
index 09dc7237..813fe371 100644
--- a/charger/solax.go
+++ b/charger/solax.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 
@@ -57,11 +58,11 @@ const (
 )
 
 func init() {
-	registry.Add("solax", NewSolaxFromConfig)
+	registry.AddCtx("solax", NewSolaxFromConfig)
 }
 
 // NewSolaxFromConfig creates a Solax charger from generic config
-func NewSolaxFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewSolaxFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.Settings{
 		ID: 1,
 	}
@@ -70,12 +71,12 @@ func NewSolaxFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewSolax(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
+	return NewSolax(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
 }
 
 // NewSolax creates Solax charger
-func NewSolax(uri, device, comset string, baudrate int, proto modbus.Protocol, id uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, id)
+func NewSolax(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, id uint8) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/sungrow.go b/charger/sungrow.go
index 50fadcb2..be824f5a 100644
--- a/charger/sungrow.go
+++ b/charger/sungrow.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 
@@ -62,11 +63,11 @@ var (
 )
 
 func init() {
-	registry.Add("sungrow", NewSungrowFromConfig)
+	registry.AddCtx("sungrow", NewSungrowFromConfig)
 }
 
 // NewSungrowFromConfig creates a Sungrow charger from generic config
-func NewSungrowFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewSungrowFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.Settings{
 		ID: 248,
 	}
@@ -75,12 +76,12 @@ func NewSungrowFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	return NewSungrow(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
+	return NewSungrow(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
 }
 
 // NewSungrow creates Sungrow charger
-func NewSungrow(uri, device, comset string, baudrate int, proto modbus.Protocol, id uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, id)
+func NewSungrow(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, id uint8) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/versicharge.go b/charger/versicharge.go
index 72bb5b96..fc907691 100644
--- a/charger/versicharge.go
+++ b/charger/versicharge.go
@@ -19,6 +19,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 
@@ -55,11 +56,11 @@ type Versicharge struct {
 }
 
 func init() {
-	registry.Add("versicharge", NewVersichargeFromConfig)
+	registry.AddCtx("versicharge", NewVersichargeFromConfig)
 }
 
 // NewVersichargeFromConfig creates a Versicharge charger from generic config
-func NewVersichargeFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewVersichargeFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: 2,
 	}
@@ -68,12 +69,12 @@ func NewVersichargeFromConfig(other map[string]interface{}) (api.Charger, error)
 		return nil, err
 	}
 
-	return NewVersicharge(cc.URI, cc.ID)
+	return NewVersicharge(ctx, cc.URI, cc.ID)
 }
 
 // NewVersicharge creates a Versicharge charger
-func NewVersicharge(uri string, id uint8) (*Versicharge, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+func NewVersicharge(ctx context.Context, uri string, id uint8) (*Versicharge, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/vestel.go b/charger/vestel.go
index ee8b1599..b9773300 100644
--- a/charger/vestel.go
+++ b/charger/vestel.go
@@ -83,7 +83,7 @@ func NewVestelFromConfig(ctx context.Context, other map[string]interface{}) (api
 
 // NewVestel creates a Vestel charger
 func NewVestel(ctx context.Context, uri string, id uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/victron.go b/charger/victron.go
index 2c4ca75d..a35a2668 100644
--- a/charger/victron.go
+++ b/charger/victron.go
@@ -18,6 +18,7 @@ package charger
 // SOFTWARE.
 
 import (
+	"context"
 	"encoding/binary"
 	"errors"
 	"fmt"
@@ -67,22 +68,22 @@ var (
 )
 
 func init() {
-	registry.Add("victron", NewVictronGXFromConfig)
-	registry.Add("victron-evcs", NewVictronEVCSFromConfig)
+	registry.AddCtx("victron", NewVictronGXFromConfig)
+	registry.AddCtx("victron-evcs", NewVictronEVCSFromConfig)
 }
 
 // NewVictronGXFromConfig creates a ABB charger from generic config
-func NewVictronGXFromConfig(other map[string]interface{}) (api.Charger, error) {
-	return NewVictronFromConfig(other, victronGX)
+func NewVictronGXFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
+	return NewVictronFromConfig(ctx, other, victronGX)
 }
 
 // NewVictronEVCSFromConfig creates a ABB charger from generic config
-func NewVictronEVCSFromConfig(other map[string]interface{}) (api.Charger, error) {
-	return NewVictronFromConfig(other, victronEVCS)
+func NewVictronEVCSFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
+	return NewVictronFromConfig(ctx, other, victronEVCS)
 }
 
 // NewVictronFromConfig creates a ABB charger from generic config
-func NewVictronFromConfig(other map[string]interface{}, regs victronRegs) (api.Charger, error) {
+func NewVictronFromConfig(ctx context.Context, other map[string]interface{}, regs victronRegs) (api.Charger, error) {
 	cc := modbus.TcpSettings{
 		ID: cast.ToUint8(regs.isGX) * 100,
 	}
@@ -91,12 +92,12 @@ func NewVictronFromConfig(other map[string]interface{}, regs victronRegs) (api.C
 		return nil, err
 	}
 
-	return NewVictron(cc.URI, cc.ID, regs)
+	return NewVictron(ctx, cc.URI, cc.ID, regs)
 }
 
 // NewVictron creates Victron charger
-func NewVictron(uri string, slaveID uint8, regs victronRegs) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
+func NewVictron(ctx context.Context, uri string, slaveID uint8, regs victronRegs) (api.Charger, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/wallbe.go b/charger/wallbe.go
index c4030e29..4c02349d 100644
--- a/charger/wallbe.go
+++ b/charger/wallbe.go
@@ -1,6 +1,7 @@
 package charger
 
 import (
+	"context"
 	"encoding/binary"
 	"fmt"
 
@@ -37,13 +38,13 @@ type Wallbe struct {
 }
 
 func init() {
-	registry.Add("wallbe", NewWallbeFromConfig)
+	registry.AddCtx("wallbe", NewWallbeFromConfig)
 }
 
 //go:generate go tool decorate -f decorateWallbe -b *Wallbe -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.ChargerEx,MaxCurrentMillis,func(float64) error"
 
 // NewWallbeFromConfig creates a Wallbe charger from generic config
-func NewWallbeFromConfig(other map[string]interface{}) (api.Charger, error) {
+func NewWallbeFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := struct {
 		URI    string
 		Legacy bool
@@ -58,7 +59,7 @@ func NewWallbeFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, err
 	}
 
-	wb, err := NewWallbe(cc.URI)
+	wb, err := NewWallbe(ctx, cc.URI)
 	if err != nil {
 		return nil, err
 	}
@@ -91,8 +92,8 @@ func NewWallbeFromConfig(other map[string]interface{}) (api.Charger, error) {
 }
 
 // NewWallbe creates a Wallbe charger
-func NewWallbe(uri string) (*Wallbe, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, wbSlaveID)
+func NewWallbe(ctx context.Context, uri string) (*Wallbe, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, wbSlaveID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/charger/webasto-next.go b/charger/webasto-next.go
index deb7c088..41e03be6 100644
--- a/charger/webasto-next.go
+++ b/charger/webasto-next.go
@@ -70,7 +70,7 @@ func NewWebastoNextFromConfig(ctx context.Context, other map[string]interface{})
 
 // NewWebastoNext creates WebastoNext charger
 func NewWebastoNext(ctx context.Context, uri string, id uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git "a/charger/weidm\303\274ller.go" "b/charger/weidm\303\274ller.go"
index 9afbae91..82345013 100644
--- "a/charger/weidm\303\274ller.go"
+++ "b/charger/weidm\303\274ller.go"
@@ -74,7 +74,7 @@ func NewWeidmüllerFromConfig(ctx context.Context, other map[string]interface{})
 
 // NewWeidmüller creates Weidmüller charger
 func NewWeidmüller(ctx context.Context, uri string, id uint8) (api.Charger, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/meter/cfos.go b/meter/cfos.go
index 7a2f635d..f5c9fc49 100644
--- a/meter/cfos.go
+++ b/meter/cfos.go
@@ -1,6 +1,7 @@
 package meter
 
 import (
+	"context"
 	"encoding/binary"
 
 	"github.com/evcc-io/evcc/api"
@@ -23,11 +24,11 @@ type CfosPowerBrain struct {
 }
 
 func init() {
-	registry.Add("cfos", NewCfosPowerBrainFromConfig)
+	registry.AddCtx("cfos", NewCfosPowerBrainFromConfig)
 }
 
 // NewCfosPowerBrainFromConfig creates a cFos meter from generic config
-func NewCfosPowerBrainFromConfig(other map[string]interface{}) (api.Meter, error) {
+func NewCfosPowerBrainFromConfig(ctx context.Context, other map[string]interface{}) (api.Meter, error) {
 	cc := modbus.TcpSettings{
 		ID: 1,
 	}
@@ -36,12 +37,12 @@ func NewCfosPowerBrainFromConfig(other map[string]interface{}) (api.Meter, error
 		return nil, err
 	}
 
-	return NewCfosPowerBrain(cc.URI, cc.ID)
+	return NewCfosPowerBrain(ctx, cc.URI, cc.ID)
 }
 
 // NewCfosPowerBrain creates a cFos meter
-func NewCfosPowerBrain(uri string, id uint8) (*CfosPowerBrain, error) {
-	conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id)
+func NewCfosPowerBrain(ctx context.Context, uri string, id uint8) (*CfosPowerBrain, error) {
+	conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
 	if err != nil {
 		return nil, err
 	}
diff --git a/meter/mbmd.go b/meter/mbmd.go
index 059a7bb9..ffd831b0 100644
--- a/meter/mbmd.go
+++ b/meter/mbmd.go
@@ -1,6 +1,7 @@
 package meter
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"strings"
@@ -24,13 +25,13 @@ type ModbusMbmd struct {
 }
 
 func init() {
-	registry.Add("mbmd", NewModbusMbmdFromConfig)
+	registry.AddCtx("mbmd", NewModbusMbmdFromConfig)
 }
 
 //go:generate go tool decorate -f decorateModbusMbmd -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.PhasePowers,Powers,func() (float64, float64, float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatteryCapacity,Capacity,func() float64"
 
 // NewModbusMbmdFromConfig creates api.Meter from config
-func NewModbusMbmdFromConfig(other map[string]interface{}) (api.Meter, error) {
+func NewModbusMbmdFromConfig(ctx context.Context, other map[string]interface{}) (api.Meter, error) {
 	cc := struct {
 		Model              string
 		capacity           `mapstructure:",squash"`
@@ -62,7 +63,7 @@ func NewModbusMbmdFromConfig(other map[string]interface{}) (api.Meter, error) {
 	modbus.Lock()
 	defer modbus.Unlock()
 
-	conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID)
+	conn, err := modbus.NewConnection(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/plugin/modbus.go b/plugin/modbus.go
index f1f016af..f73f95e6 100644
--- a/plugin/modbus.go
+++ b/plugin/modbus.go
@@ -45,7 +45,7 @@ func NewModbusFromConfig(ctx context.Context, other map[string]interface{}) (Plu
 	modbus.Lock()
 	defer modbus.Unlock()
 
-	conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID)
+	conn, err := modbus.NewConnection(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/plugin/sunspec.go b/plugin/sunspec.go
index b956fac6..1813fbbb 100644
--- a/plugin/sunspec.go
+++ b/plugin/sunspec.go
@@ -1,6 +1,7 @@
 package plugin
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"math"
@@ -24,11 +25,11 @@ type ModbusSunspec struct {
 }
 
 func init() {
-	registry.Add("sunspec", NewModbusSunspecFromConfig)
+	registry.AddCtx("sunspec", NewModbusSunspecFromConfig)
 }
 
 // NewModbusSunspecFromConfig creates Modbus plugin
-func NewModbusSunspecFromConfig(other map[string]interface{}) (Plugin, error) {
+func NewModbusSunspecFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	cc := struct {
 		modbus.Settings `mapstructure:",squash"`
 		Value           []string
@@ -47,7 +48,7 @@ func NewModbusSunspecFromConfig(other map[string]interface{}) (Plugin, error) {
 	modbus.Lock()
 	defer modbus.Unlock()
 
-	conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID)
+	conn, err := modbus.NewConnection(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID)
 	if err != nil {
 		return nil, err
 	}
diff --git a/server/modbus/proxy.go b/server/modbus/proxy.go
index 12123432..d364c397 100644
--- a/server/modbus/proxy.go
+++ b/server/modbus/proxy.go
@@ -1,6 +1,7 @@
 package modbus
 
 import (
+	"context"
 	"fmt"
 	"net"
 
@@ -12,7 +13,7 @@ import (
 )
 
 func StartProxy(port int, config modbus.Settings, readOnly ReadOnlyMode) error {
-	conn, err := modbus.NewConnection(config.URI, config.Device, config.Comset, config.Baudrate, config.Protocol(), config.ID)
+	conn, err := modbus.NewConnection(context.Background(), config.URI, config.Device, config.Comset, config.Baudrate, config.Protocol(), config.ID)
 	if err != nil {
 		return err
 	}
diff --git a/util/modbus/modbus.go b/util/modbus/modbus.go
index 52dcaae3..4f4ca847 100644
--- a/util/modbus/modbus.go
+++ b/util/modbus/modbus.go
@@ -1,6 +1,7 @@
 package modbus
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"strings"
@@ -62,6 +63,7 @@ func (s *Settings) String() string {
 type meterConnection struct {
 	meters.Connection
 	proto Protocol
+	refs  int // count of references; first connection has ref count 0
 	*logger
 }
 
@@ -70,7 +72,24 @@ var (
 	mu          sync.Mutex
 )
 
-func registeredConnection(key string, proto Protocol, newConn meters.Connection) (*meterConnection, error) {
+func unregisterConnection(key string) {
+	mu.Lock()
+	defer mu.Unlock()
+
+	conn, ok := connections[key]
+	if !ok {
+		panic("unregisterConnection: connection not found " + key)
+	}
+
+	if conn.refs > 0 {
+		conn.refs--
+		return
+	}
+
+	delete(connections, key)
+}
+
+func registeredConnection(ctx context.Context, key string, proto Protocol, newConn meters.Connection) (*meterConnection, error) {
 	mu.Lock()
 	defer mu.Unlock()
 
@@ -79,9 +98,16 @@ func registeredConnection(key string, proto Protocol, newConn meters.Connection)
 			return nil, fmt.Errorf("connection already registered with different protocol: %s", key)
 		}
 
+		conn.refs++
+
 		return conn, nil
 	}
 
+	go func() {
+		<-ctx.Done()
+		unregisterConnection(key)
+	}()
+
 	connection := &meterConnection{
 		Connection: newConn,
 		proto:      proto,
@@ -95,8 +121,8 @@ func registeredConnection(key string, proto Protocol, newConn meters.Connection)
 }
 
 // NewConnection creates physical modbus device from config
-func NewConnection(uri, device, comset string, baudrate int, proto Protocol, slaveID uint8) (*Connection, error) {
-	conn, err := physicalConnection(proto, Settings{
+func NewConnection(ctx context.Context, uri, device, comset string, baudrate int, proto Protocol, slaveID uint8) (*Connection, error) {
+	conn, err := physicalConnection(ctx, proto, Settings{
 		URI:      uri,
 		Device:   device,
 		Comset:   comset,
@@ -115,7 +141,7 @@ func NewConnection(uri, device, comset string, baudrate int, proto Protocol, sla
 	return res, nil
 }
 
-func physicalConnection(proto Protocol, cfg Settings) (*meterConnection, error) {
+func physicalConnection(ctx context.Context, proto Protocol, cfg Settings) (*meterConnection, error) {
 	if (cfg.Device != "") == (cfg.URI != "") {
 		return nil, errors.New("invalid modbus configuration: must have either uri or device")
 	}
@@ -135,9 +161,9 @@ func physicalConnection(proto Protocol, cfg Settings) (*meterConnection, error)
 
 		switch proto {
 		case Ascii:
-			return registeredConnection(cfg.Device, proto, meters.NewASCII(cfg.Device, cfg.Baudrate, cfg.Comset))
+			return registeredConnection(ctx, cfg.Device, proto, meters.NewASCII(cfg.Device, cfg.Baudrate, cfg.Comset))
 		default:
-			return registeredConnection(cfg.Device, proto, meters.NewRTU(cfg.Device, cfg.Baudrate, cfg.Comset))
+			return registeredConnection(ctx, cfg.Device, proto, meters.NewRTU(cfg.Device, cfg.Baudrate, cfg.Comset))
 		}
 	}
 
@@ -145,12 +171,12 @@ func physicalConnection(proto Protocol, cfg Settings) (*meterConnection, error)
 
 	switch proto {
 	case Udp:
-		return registeredConnection(uri, proto, meters.NewRTUOverUDP(uri))
+		return registeredConnection(ctx, uri, proto, meters.NewRTUOverUDP(uri))
 	case Rtu:
-		return registeredConnection(uri, proto, meters.NewRTUOverTCP(uri))
+		return registeredConnection(ctx, uri, proto, meters.NewRTUOverTCP(uri))
 	case Ascii:
-		return registeredConnection(uri, proto, meters.NewASCIIOverTCP(uri))
+		return registeredConnection(ctx, uri, proto, meters.NewASCIIOverTCP(uri))
 	default:
-		return registeredConnection(uri, proto, meters.NewTCP(uri))
+		return registeredConnection(ctx, uri, proto, meters.NewTCP(uri))
 	}
 }


Test patch:
diff --git a/server/modbus/proxy_test.go b/server/modbus/proxy_test.go
index 5eb258dc..4947c563 100644
--- a/server/modbus/proxy_test.go
+++ b/server/modbus/proxy_test.go
@@ -1,6 +1,7 @@
 package modbus
 
 import (
+	"context"
 	"encoding/binary"
 	"math/rand"
 	"net"
@@ -34,7 +35,7 @@ func TestConcurrentRead(t *testing.T) {
 
 		go func(id int) {
 			// client
-			conn, err := modbus.NewConnection(l.Addr().String(), "", "", 0, modbus.Tcp, uint8(id))
+			conn, err := modbus.NewConnection(context.TODO(), l.Addr().String(), "", "", 0, modbus.Tcp, uint8(id))
 			require.NoError(t, err)
 
 			for range 50 {
@@ -78,7 +79,7 @@ func TestReadCoils(t *testing.T) {
 	require.NoError(t, err)
 	defer pl.Close()
 
-	downstreamConn, err := modbus.NewConnection(l.Addr().String(), "", "", 0, modbus.Tcp, 1)
+	downstreamConn, err := modbus.NewConnection(context.TODO(), l.Addr().String(), "", "", 0, modbus.Tcp, 1)
 	require.NoError(t, err)
 
 	proxy, _ := mbserver.New(&handler{
@@ -90,7 +91,7 @@ func TestReadCoils(t *testing.T) {
 
 	// test client
 	{
-		conn, err := modbus.NewConnection(pl.Addr().String(), "", "", 0, modbus.Tcp, 1)
+		conn, err := modbus.NewConnection(context.TODO(), pl.Addr().String(), "", "", 0, modbus.Tcp, 1)
 		require.NoError(t, err)
 
 		{ // read