Repository: evcc-io/evcc
Pull request #18335 (2025-01-21 17:05:48)

chore: refactor plugin structure
Consistently rename `provider` to `plugin`, in line with documentation. Makes it easier for new developers to understand the code base.


Patch:
diff --git a/api/globalconfig/types.go b/api/globalconfig/types.go
index 8e9b6e30..611852ce 100644
--- a/api/globalconfig/types.go
+++ b/api/globalconfig/types.go
@@ -7,7 +7,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider/mqtt"
+	"github.com/evcc-io/evcc/plugin/mqtt"
 	"github.com/evcc-io/evcc/push"
 	"github.com/evcc-io/evcc/server/eebus"
 	"github.com/evcc-io/evcc/util/config"
diff --git a/charger/charger.go b/charger/charger.go
index d8278a96..b8f60a7b 100644
--- a/charger/charger.go
+++ b/charger/charger.go
@@ -7,7 +7,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/meter"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -30,40 +30,40 @@ func init() {
 func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	var cc struct {
 		embed                               `mapstructure:",squash"`
-		Status, Enable, Enabled, MaxCurrent provider.Config
-		MaxCurrentMillis                    *provider.Config
-		Identify, Phases1p3p                *provider.Config
-		Wakeup                              *provider.Config
-		Soc                                 *provider.Config
+		Status, Enable, Enabled, MaxCurrent plugin.Config
+		MaxCurrentMillis                    *plugin.Config
+		Identify, Phases1p3p                *plugin.Config
+		Wakeup                              *plugin.Config
+		Soc                                 *plugin.Config
 		Tos                                 bool
 
 		// optional measurements
-		Power  *provider.Config
-		Energy *provider.Config
+		Power  *plugin.Config
+		Energy *plugin.Config
 
-		Currents, Voltages []provider.Config
+		Currents, Voltages []plugin.Config
 	}
 
 	if err := util.DecodeOther(other, &cc); err != nil {
 		return nil, err
 	}
 
-	status, err := provider.NewStringGetterFromConfig(ctx, cc.Status)
+	status, err := cc.Status.StringGetter(ctx)
 	if err != nil {
 		return nil, fmt.Errorf("status: %w", err)
 	}
 
-	enabled, err := provider.NewBoolGetterFromConfig(ctx, cc.Enabled)
+	enabled, err := cc.Enabled.BoolGetter(ctx)
 	if err != nil {
 		return nil, fmt.Errorf("enabled: %w", err)
 	}
 
-	enable, err := provider.NewBoolSetterFromConfig(ctx, "enable", cc.Enable)
+	enable, err := cc.Enable.BoolSetter(ctx, "enable")
 	if err != nil {
 		return nil, fmt.Errorf("enable: %w", err)
 	}
 
-	maxcurrent, err := provider.NewIntSetterFromConfig(ctx, "maxcurrent", cc.MaxCurrent)
+	maxcurrent, err := cc.MaxCurrent.IntSetter(ctx, "maxcurrent")
 	if err != nil {
 		return nil, fmt.Errorf("maxcurrent: %w", err)
 	}
@@ -75,12 +75,9 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 
 	c.embed = &cc.embed
 
-	var maxcurrentmillis func(float64) error
-	if cc.MaxCurrentMillis != nil {
-		maxcurrentmillis, err = provider.NewFloatSetterFromConfig(ctx, "maxcurrentmillis", *cc.MaxCurrentMillis)
-		if err != nil {
-			return nil, fmt.Errorf("maxcurrentmillis: %w", err)
-		}
+	maxcurrentmillis, err := cc.MaxCurrentMillis.FloatSetter(ctx, "maxcurrentmillis")
+	if err != nil {
+		return nil, fmt.Errorf("maxcurrentmillis: %w", err)
 	}
 
 	// decorate phases
@@ -90,7 +87,7 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 			return nil, errors.New("1p3p does no longer handle disable/enable. Use tos: true to confirm you understand the consequences")
 		}
 
-		phases1p3pS, err := provider.NewIntSetterFromConfig(ctx, "phases", *cc.Phases1p3p)
+		phases1p3pS, err := cc.Phases1p3p.IntSetter(ctx, "phases")
 		if err != nil {
 			return nil, fmt.Errorf("phases: %w", err)
 		}
@@ -101,34 +98,28 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 	}
 
 	// decorate identifier
-	var identify func() (string, error)
-	if cc.Identify != nil {
-		identify, err = provider.NewStringGetterFromConfig(ctx, *cc.Identify)
-		if err != nil {
-			return nil, fmt.Errorf("identify: %w", err)
-		}
+	identify, err := cc.Identify.StringGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("identify: %w", err)
 	}
 
 	// decorate wakeup
 	var wakeup func() error
 	if cc.Wakeup != nil {
-		wakeupS, err := provider.NewBoolSetterFromConfig(ctx, "wakeup", *cc.Wakeup)
+		set, err := cc.Wakeup.BoolSetter(ctx, "wakeup")
 		if err != nil {
 			return nil, fmt.Errorf("wakeup: %w", err)
 		}
 
 		wakeup = func() error {
-			return wakeupS(true)
+			return set(true)
 		}
 	}
 
 	// decorate soc
-	var soc func() (float64, error)
-	if cc.Soc != nil {
-		soc, err = provider.NewFloatGetterFromConfig(ctx, *cc.Soc)
-		if err != nil {
-			return nil, fmt.Errorf("soc: %w", err)
-		}
+	soc, err := cc.Soc.FloatGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("soc: %w", err)
 	}
 
 	// decorate measurements
diff --git a/charger/connectiq.go b/charger/connectiq.go
index 55f98e5d..33337049 100644
--- a/charger/connectiq.go
+++ b/charger/connectiq.go
@@ -25,7 +25,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/connectiq"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 )
@@ -71,7 +71,7 @@ func NewConnectIq(uri string, cache time.Duration) (api.Charger, error) {
 	}
 
 	// cache meter readings
-	wb.meterG = provider.Cached(func() (connectiq.MeterStatus, error) {
+	wb.meterG = plugin.Cached(func() (connectiq.MeterStatus, error) {
 		var res connectiq.MeterStatus
 		uri := fmt.Sprintf("%s/meter/status", wb.uri)
 		err := wb.GetJSON(uri, &res)
diff --git a/charger/daheimladen.go b/charger/daheimladen.go
index b75acefa..1ae40880 100644
--- a/charger/daheimladen.go
+++ b/charger/daheimladen.go
@@ -8,7 +8,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/daheimladen"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"golang.org/x/oauth2"
@@ -22,8 +22,8 @@ type DaheimLaden struct {
 	idTag         string
 	token         string
 	transactionID int32
-	statusG       provider.Cacheable[daheimladen.GetLatestStatus]
-	meterG        provider.Cacheable[daheimladen.GetLatestMeterValueResponse]
+	statusG       plugin.Cacheable[daheimladen.GetLatestStatus]
+	meterG        plugin.Cacheable[daheimladen.GetLatestMeterValueResponse]
 	cache         time.Duration
 }
 
@@ -67,13 +67,13 @@ func NewDaheimLaden(token, stationID string, cache time.Duration) (*DaheimLaden,
 		Base: c.Client.Transport,
 	}
 
-	c.statusG = provider.ResettableCached(func() (daheimladen.GetLatestStatus, error) {
+	c.statusG = plugin.ResettableCached(func() (daheimladen.GetLatestStatus, error) {
 		var res daheimladen.GetLatestStatus
 		err := c.GetJSON(fmt.Sprintf("%s/cs/%s/status", daheimladen.BASE_URL, c.stationID), &res)
 		return res, err
 	}, c.cache)
 
-	c.meterG = provider.ResettableCached(func() (daheimladen.GetLatestMeterValueResponse, error) {
+	c.meterG = plugin.ResettableCached(func() (daheimladen.GetLatestMeterValueResponse, error) {
 		var res daheimladen.GetLatestMeterValueResponse
 		err := c.GetJSON(fmt.Sprintf("%s/cs/%s/metervalue", daheimladen.BASE_URL, c.stationID), &res)
 		return res, err
diff --git a/charger/eebus.go b/charger/eebus.go
index 633b863b..522f346b 100644
--- a/charger/eebus.go
+++ b/charger/eebus.go
@@ -15,7 +15,7 @@ import (
 	"github.com/enbility/spine-go/model"
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/core/loadpoint"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/server/eebus"
 	"github.com/evcc-io/evcc/util"
 	"github.com/samber/lo"
@@ -88,7 +88,7 @@ func NewEEBus(ski, ip string, hasMeter, hasChargedEnergy, vasVW bool) (api.Charg
 	}
 
 	c.Connector = eebus.NewConnector()
-	c.minMaxG = provider.Cached(c.minMax, time.Second)
+	c.minMaxG = plugin.Cached(c.minMax, time.Second)
 
 	if err := eebus.Instance.RegisterDevice(ski, ip, c); err != nil {
 		return nil, err
diff --git a/charger/evsewifi.go b/charger/evsewifi.go
index 6775dbef..aa3254d0 100644
--- a/charger/evsewifi.go
+++ b/charger/evsewifi.go
@@ -8,7 +8,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/evse"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 )
@@ -20,7 +20,7 @@ type EVSEWifi struct {
 	alwaysActive bool
 	current      int64 // current will always be the physical value sent to the API
 	hires        bool
-	paramG       provider.Cacheable[evse.ListEntry]
+	paramG       plugin.Cacheable[evse.ListEntry]
 }
 
 func init() {
@@ -122,7 +122,7 @@ func NewEVSEWifi(uri string, cache time.Duration) (*EVSEWifi, error) {
 		current: 6, // 6A defined value
 	}
 
-	wb.paramG = provider.ResettableCached(func() (evse.ListEntry, error) {
+	wb.paramG = plugin.ResettableCached(func() (evse.ListEntry, error) {
 		var res evse.ParameterResponse
 		uri := fmt.Sprintf("%s/getParameters", wb.uri)
 		if err := wb.GetJSON(uri, &res); err != nil {
diff --git a/charger/hardybarth-ecb1.go b/charger/hardybarth-ecb1.go
index 2102db2c..eb2f2a54 100644
--- a/charger/hardybarth-ecb1.go
+++ b/charger/hardybarth-ecb1.go
@@ -30,7 +30,7 @@ import (
 	"github.com/evcc-io/evcc/charger/echarge"
 	"github.com/evcc-io/evcc/charger/echarge/ecb1"
 	"github.com/evcc-io/evcc/meter/obis"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/sponsor"
@@ -85,7 +85,7 @@ func NewHardyBarth(uri string, chargecontrol, meter int, cache time.Duration) (a
 	}
 
 	// cache meter readings
-	wb.meterG = provider.Cached(func() (ecb1.Meter, error) {
+	wb.meterG = plugin.Cached(func() (ecb1.Meter, error) {
 		var res struct {
 			Meter struct {
 				ecb1.Meter
diff --git a/charger/hardybarth-salia.go b/charger/hardybarth-salia.go
index 1763b84c..63880b63 100644
--- a/charger/hardybarth-salia.go
+++ b/charger/hardybarth-salia.go
@@ -30,7 +30,7 @@ import (
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/echarge"
 	"github.com/evcc-io/evcc/charger/echarge/salia"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/sponsor"
@@ -46,7 +46,7 @@ type Salia struct {
 	uri     string
 	current int64
 	fw      int // 2 if fw 2.0
-	apiG    provider.Cacheable[salia.Api]
+	apiG    plugin.Cacheable[salia.Api]
 }
 
 func init() {
@@ -84,7 +84,7 @@ func NewSalia(ctx context.Context, uri string, cache time.Duration) (api.Charger
 		current: 6,
 	}
 
-	wb.apiG = provider.ResettableCached(func() (salia.Api, error) {
+	wb.apiG = plugin.ResettableCached(func() (salia.Api, error) {
 		var res salia.Api
 		err := wb.GetJSON(wb.uri, &res)
 		return res, err
diff --git a/charger/mystrom.go b/charger/mystrom.go
index bf9fdac2..504d5dd7 100644
--- a/charger/mystrom.go
+++ b/charger/mystrom.go
@@ -6,7 +6,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/meter/mystrom"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -21,7 +21,7 @@ func init() {
 type MyStrom struct {
 	*switchSocket
 	conn    *mystrom.Connection
-	reportG provider.Cacheable[mystrom.Report]
+	reportG plugin.Cacheable[mystrom.Report]
 }
 
 // NewMyStromFromConfig creates a myStrom charger from generic config
@@ -44,7 +44,7 @@ func NewMyStromFromConfig(other map[string]interface{}) (api.Charger, error) {
 	}
 
 	c.switchSocket = NewSwitchSocket(&cc.embed, c.Enabled, c.conn.CurrentPower, cc.StandbyPower)
-	c.reportG = provider.ResettableCached(c.conn.Report, cc.Cache)
+	c.reportG = plugin.ResettableCached(c.conn.Report, cc.Cache)
 
 	return c, nil
 }
diff --git a/charger/nrgconnect.go b/charger/nrgconnect.go
index 5e84b796..cffc63d8 100644
--- a/charger/nrgconnect.go
+++ b/charger/nrgconnect.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/nrg/connect"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 )
@@ -23,8 +23,8 @@ type NRGKickConnect struct {
 	mac           string
 	password      string
 	enabled       bool
-	settingsG     provider.Cacheable[connect.Settings]
-	measurementsG provider.Cacheable[connect.Measurements]
+	settingsG     plugin.Cacheable[connect.Settings]
+	measurementsG plugin.Cacheable[connect.Measurements]
 }
 
 func init() {
@@ -56,7 +56,7 @@ func NewNRGKickConnect(uri, mac, password string, cache time.Duration) (*NRGKick
 		password: password,
 	}
 
-	nrg.settingsG = provider.ResettableCached(func() (connect.Settings, error) {
+	nrg.settingsG = plugin.ResettableCached(func() (connect.Settings, error) {
 		var res connect.Settings
 
 		err := nrg.GetJSON(nrg.apiURL(connect.SettingsPath), &res)
@@ -67,7 +67,7 @@ func NewNRGKickConnect(uri, mac, password string, cache time.Duration) (*NRGKick
 		return res, err
 	}, cache)
 
-	nrg.measurementsG = provider.ResettableCached(func() (connect.Measurements, error) {
+	nrg.measurementsG = plugin.ResettableCached(func() (connect.Measurements, error) {
 		var res connect.Measurements
 
 		err := nrg.GetJSON(nrg.apiURL(connect.MeasurementsPath), &res)
diff --git a/charger/openevse.go b/charger/openevse.go
index dfa5f003..bf7e171e 100644
--- a/charger/openevse.go
+++ b/charger/openevse.go
@@ -10,7 +10,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/openevse"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/transport"
@@ -20,7 +20,7 @@ import (
 type OpenEVSE struct {
 	*request.Helper
 	uri     string
-	statusG provider.Cacheable[openevse.Status]
+	statusG plugin.Cacheable[openevse.Status]
 	current int
 	enabled bool
 }
@@ -67,7 +67,7 @@ func NewOpenEVSE(uri, user, password string, cache time.Duration) (api.Charger,
 		c.Client.Transport = transport.BasicAuth(user, password, c.Client.Transport)
 	}
 
-	c.statusG = provider.ResettableCached(func() (openevse.Status, error) {
+	c.statusG = plugin.ResettableCached(func() (openevse.Status, error) {
 		var res openevse.Status
 
 		uri := fmt.Sprintf("%s/status", c.uri)
diff --git a/charger/openwb-pro.go b/charger/openwb-pro.go
index 1feec1ca..5a53dd2c 100644
--- a/charger/openwb-pro.go
+++ b/charger/openwb-pro.go
@@ -8,7 +8,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/openwb/pro"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 )
@@ -24,7 +24,7 @@ type OpenWBPro struct {
 	*request.Helper
 	uri     string
 	current float64
-	statusG provider.Cacheable[pro.Status]
+	statusG plugin.Cacheable[pro.Status]
 }
 
 // NewOpenWBProFromConfig creates a OpenWBPro charger from generic config
@@ -53,7 +53,7 @@ func NewOpenWBPro(ctx context.Context, uri string, cache time.Duration) (*OpenWB
 		current: 6, // 6A defined value
 	}
 
-	wb.statusG = provider.ResettableCached(func() (pro.Status, error) {
+	wb.statusG = plugin.ResettableCached(func() (pro.Status, error) {
 		var res pro.Status
 		uri := fmt.Sprintf("%s/%s", wb.uri, "connect.php")
 		err := wb.GetJSON(uri, &res)
diff --git a/charger/openwb.go b/charger/openwb.go
index e4de91d0..99e59112 100644
--- a/charger/openwb.go
+++ b/charger/openwb.go
@@ -8,8 +8,8 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/openwb"
-	"github.com/evcc-io/evcc/provider"
-	"github.com/evcc-io/evcc/provider/mqtt"
+	"github.com/evcc-io/evcc/plugin"
+	"github.com/evcc-io/evcc/plugin/mqtt"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -63,18 +63,18 @@ func NewOpenWB(log *util.Logger, mqttconf mqtt.Config, id int, topic string, p1p
 	}
 
 	// timeout handler
-	h, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/system/%s", topic, openwb.TimestampTopic), timeout).StringGetter()
+	h, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/system/%s", topic, openwb.TimestampTopic), timeout).StringGetter()
 	if err != nil {
 		return nil, err
 	}
-	to := provider.NewTimeoutHandler(h)
+	to := plugin.NewTimeoutHandler(h)
 
-	mq := func(subtopic string) *provider.Mqtt {
-		return provider.NewMqtt(log, client, fmt.Sprintf("%s/lp/%d/%s", topic, id, subtopic), 0)
+	mq := func(subtopic string) *plugin.Mqtt {
+		return plugin.NewMqtt(log, client, fmt.Sprintf("%s/lp/%d/%s", topic, id, subtopic), 0)
 	}
 
 	// check if loadpoint configured
-	configured, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/lp/%d/%s", topic, id, openwb.ConfiguredTopic), timeout).BoolGetter()
+	configured, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/lp/%d/%s", topic, id, openwb.ConfiguredTopic), timeout).BoolGetter()
 	if err != nil {
 		return nil, err
 	}
@@ -91,7 +91,7 @@ func NewOpenWB(log *util.Logger, mqttconf mqtt.Config, id int, topic string, p1p
 	if err != nil {
 		return nil, err
 	}
-	statusG, err := provider.NewCombinedProvider(pluggedG, chargingG).StringGetter()
+	statusG, err := plugin.NewCombinedPlugin(pluggedG, chargingG).StringGetter()
 	if err != nil {
 		return nil, err
 	}
@@ -102,7 +102,7 @@ func NewOpenWB(log *util.Logger, mqttconf mqtt.Config, id int, topic string, p1p
 		// TODO remove after https://github.com/snaptec/openWB/issues/1757
 		currentTopic = "Lp2" + openwb.SlaveChargeCurrentTopic
 	}
-	currentS, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/set/isss/%s", topic, currentTopic),
+	currentS, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/set/isss/%s", topic, currentTopic),
 		timeout).WithRetained().IntSetter("current")
 	if err != nil {
 		return nil, err
@@ -113,13 +113,13 @@ func NewOpenWB(log *util.Logger, mqttconf mqtt.Config, id int, topic string, p1p
 		// TODO remove after https://github.com/snaptec/openWB/issues/1757
 		cpTopic = strings.TrimSuffix(cpTopic, "1") + "2"
 	}
-	wakeupS, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/set/isss/%s", topic, cpTopic),
+	wakeupS, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/set/isss/%s", topic, cpTopic),
 		timeout).WithRetained().IntSetter("cp")
 	if err != nil {
 		return nil, err
 	}
 
-	authS, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/set/chargepoint/%d/set/%s", topic, id, openwb.RfidTopic),
+	authS, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/set/chargepoint/%d/set/%s", topic, id, openwb.RfidTopic),
 		timeout).WithRetained().StringSetter("rfid")
 	if err != nil {
 		return nil, err
@@ -155,7 +155,7 @@ func NewOpenWB(log *util.Logger, mqttconf mqtt.Config, id int, topic string, p1p
 	}
 
 	// heartbeat
-	heartbeatS, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/set/isss/%s", topic, openwb.SlaveHeartbeatTopic),
+	heartbeatS, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/set/isss/%s", topic, openwb.SlaveHeartbeatTopic),
 		timeout).WithRetained().IntSetter("heartbeat")
 	if err != nil {
 		return nil, err
@@ -178,7 +178,7 @@ func NewOpenWB(log *util.Logger, mqttconf mqtt.Config, id int, topic string, p1p
 			// TODO remove after https://github.com/snaptec/openWB/issues/1757
 			phasesTopic += "Lp2"
 		}
-		phasesS, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/set/isss/%s", topic, phasesTopic),
+		phasesS, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/set/isss/%s", topic, phasesTopic),
 			timeout).WithRetained().IntSetter("phases")
 		if err != nil {
 			return nil, err
diff --git a/charger/sgready.go b/charger/sgready.go
index 34aeaf54..3c39efb5 100644
--- a/charger/sgready.go
+++ b/charger/sgready.go
@@ -23,7 +23,7 @@ import (
 	"fmt"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -54,13 +54,13 @@ const (
 func NewSgReadyFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	cc := struct {
 		embed     `mapstructure:",squash"`
-		SetMode   provider.Config
-		GetMode   *provider.Config // optional
-		MaxPower  *provider.Config // optional
-		Power     *provider.Config // optional
-		Energy    *provider.Config // optional
-		Temp      *provider.Config // optional
-		LimitTemp *provider.Config // optional
+		SetMode   plugin.Config
+		GetMode   *plugin.Config // optional
+		MaxPower  *plugin.Config // optional
+		Power     *plugin.Config // optional
+		Energy    *plugin.Config // optional
+		Temp      *plugin.Config // optional
+		LimitTemp *plugin.Config // optional
 		Phases    int
 	}{
 		embed: embed{
@@ -74,25 +74,19 @@ func NewSgReadyFromConfig(ctx context.Context, other map[string]interface{}) (ap
 		return nil, err
 	}
 
-	set, err := provider.NewIntSetterFromConfig(ctx, "mode", cc.SetMode)
+	set, err := cc.SetMode.IntSetter(ctx, "mode")
 	if err != nil {
 		return nil, err
 	}
 
-	var get func() (int64, error)
-	if cc.GetMode != nil {
-		get, err = provider.NewIntGetterFromConfig(ctx, *cc.GetMode)
-		if err != nil {
-			return nil, err
-		}
+	get, err := cc.GetMode.IntGetter(ctx)
+	if err != nil {
+		return nil, err
 	}
 
-	var maxPower func(int64) error
-	if cc.MaxPower != nil {
-		maxPower, err = provider.NewIntSetterFromConfig(ctx, "maxpower", *cc.MaxPower)
-		if err != nil {
-			return nil, err
-		}
+	maxPower, err := cc.MaxPower.IntSetter(ctx, "maxpower")
+	if err != nil {
+		return nil, err
 	}
 
 	res, err := NewSgReady(ctx, &cc.embed, set, get, maxPower, cc.Phases)
@@ -101,38 +95,26 @@ func NewSgReadyFromConfig(ctx context.Context, other map[string]interface{}) (ap
 	}
 
 	// decorate power
-	var powerG func() (float64, error)
-	if cc.Power != nil {
-		powerG, err = provider.NewFloatGetterFromConfig(ctx, *cc.Power)
-		if err != nil {
-			return nil, fmt.Errorf("power: %w", err)
-		}
+	powerG, err := cc.Power.FloatGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("power: %w", err)
 	}
 
 	// decorate energy
-	var energyG func() (float64, error)
-	if cc.Energy != nil {
-		energyG, err = provider.NewFloatGetterFromConfig(ctx, *cc.Energy)
-		if err != nil {
-			return nil, fmt.Errorf("energy: %w", err)
-		}
+	energyG, err := cc.Energy.FloatGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("energy: %w", err)
 	}
 
 	// decorate temp
-	var tempG func() (float64, error)
-	if cc.Temp != nil {
-		tempG, err = provider.NewFloatGetterFromConfig(ctx, *cc.Temp)
-		if err != nil {
-			return nil, fmt.Errorf("temp: %w", err)
-		}
-	}
-
-	var limitTempG func() (int64, error)
-	if cc.LimitTemp != nil {
-		limitTempG, err = provider.NewIntGetterFromConfig(ctx, *cc.LimitTemp)
-		if err != nil {
-			return nil, fmt.Errorf("limit temp: %w", err)
-		}
+	tempG, err := cc.Temp.FloatGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("temp: %w", err)
+	}
+
+	limitTempG, err := cc.LimitTemp.IntGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("limit temp: %w", err)
 	}
 
 	return decorateSgReady(res, powerG, energyG, tempG, limitTempG), nil
diff --git a/charger/smaevcharger.go b/charger/smaevcharger.go
index b25555f6..cd32f098 100644
--- a/charger/smaevcharger.go
+++ b/charger/smaevcharger.go
@@ -26,7 +26,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/smaevcharger"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/sponsor"
@@ -41,8 +41,8 @@ type Smaevcharger struct {
 	uri          string // 192.168.XXX.XXX
 	cache        time.Duration
 	oldstate     float64
-	measurementG provider.Cacheable[[]smaevcharger.Measurements]
-	parameterG   provider.Cacheable[[]smaevcharger.Parameters]
+	measurementG plugin.Cacheable[[]smaevcharger.Measurements]
+	parameterG   plugin.Cacheable[[]smaevcharger.Parameters]
 }
 
 func init() {
@@ -95,8 +95,8 @@ func NewSmaevcharger(uri, user, password string, cache time.Duration) (api.Charg
 	}
 
 	// setup cached values
-	wb.measurementG = provider.ResettableCached(wb._measurementData, wb.cache)
-	wb.parameterG = provider.ResettableCached(wb._parameterData, wb.cache)
+	wb.measurementG = plugin.ResettableCached(wb._measurementData, wb.cache)
+	wb.parameterG = plugin.ResettableCached(wb._parameterData, wb.cache)
 
 	ts, err := smaevcharger.TokenSource(log, wb.uri, user, password)
 	if err != nil {
diff --git a/charger/switchsocket.go b/charger/switchsocket.go
index 65b903f1..71c9db05 100644
--- a/charger/switchsocket.go
+++ b/charger/switchsocket.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/core/loadpoint"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -24,11 +24,11 @@ type SwitchSocket struct {
 func NewSwitchSocketFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
 	var cc struct {
 		embed        `mapstructure:",squash"`
-		Enabled      provider.Config
-		Enable       provider.Config
-		Power        provider.Config
-		Energy       *provider.Config
-		Soc          *provider.Config
+		Enabled      plugin.Config
+		Enable       plugin.Config
+		Power        plugin.Config
+		Energy       *plugin.Config
+		Soc          *plugin.Config
 		StandbyPower float64
 	}
 
@@ -36,35 +36,29 @@ func NewSwitchSocketFromConfig(ctx context.Context, other map[string]interface{}
 		return nil, err
 	}
 
-	enabled, err := provider.NewBoolGetterFromConfig(ctx, cc.Enabled)
+	enabled, err := cc.Enabled.BoolGetter(ctx)
 	if err != nil {
 		return nil, err
 	}
 
-	enable, err := provider.NewBoolSetterFromConfig(ctx, "enable", cc.Enable)
+	enable, err := cc.Enable.BoolSetter(ctx, "enable")
 	if err != nil {
 		return nil, err
 	}
 
-	power, err := provider.NewFloatGetterFromConfig(ctx, cc.Power)
+	power, err := cc.Power.FloatGetter(ctx)
 	if err != nil {
 		return nil, err
 	}
 
-	var energy func() (float64, error)
-	if cc.Energy != nil {
-		energy, err = provider.NewFloatGetterFromConfig(ctx, *cc.Energy)
-		if err != nil {
-			return nil, err
-		}
+	energy, err := cc.Energy.FloatGetter(ctx)
+	if err != nil {
+		return nil, err
 	}
 
-	var soc func() (float64, error)
-	if cc.Soc != nil {
-		soc, err = provider.NewFloatGetterFromConfig(ctx, *cc.Soc)
-		if err != nil {
-			return nil, err
-		}
+	soc, err := cc.Soc.FloatGetter(ctx)
+	if err != nil {
+		return nil, err
 	}
 
 	c := &SwitchSocket{
diff --git a/charger/trydan.go b/charger/trydan.go
index 38f7205a..f9534caa 100644
--- a/charger/trydan.go
+++ b/charger/trydan.go
@@ -9,7 +9,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/sponsor"
@@ -43,7 +43,7 @@ type RealTimeData struct {
 type Trydan struct {
 	*request.Helper
 	uri     string
-	statusG provider.Cacheable[RealTimeData]
+	statusG plugin.Cacheable[RealTimeData]
 	current int
 	enabled bool
 }
@@ -83,7 +83,7 @@ func NewTrydan(uri string, cache time.Duration) (api.Charger, error) {
 		uri:    util.DefaultScheme(strings.TrimSuffix(uri, "/"), "http"),
 	}
 
-	c.statusG = provider.ResettableCached(func() (RealTimeData, error) {
+	c.statusG = plugin.ResettableCached(func() (RealTimeData, error) {
 		var res RealTimeData
 		uri := fmt.Sprintf("%s/RealTimeData", c.uri)
 		err := c.GetJSON(uri, &res)
diff --git a/charger/twc3.go b/charger/twc3.go
index 9c34a4cf..2372698b 100644
--- a/charger/twc3.go
+++ b/charger/twc3.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/core/loadpoint"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 )
@@ -73,7 +73,7 @@ func NewTwc3FromConfig(other map[string]interface{}) (api.Charger, error) {
 	client := request.NewHelper(util.NewLogger("twc3"))
 	uri := fmt.Sprintf("%s/api/1/vitals", util.DefaultScheme(strings.TrimSuffix(cc.URI, "/"), "http"))
 
-	c.vitalsG = provider.Cached(func() (Vitals, error) {
+	c.vitalsG = plugin.Cached(func() (Vitals, error) {
 		var res Vitals
 		err := client.GetJSON(uri, &res)
 		return res, err
diff --git a/charger/vaillant.go b/charger/vaillant.go
index f3b7d027..87aa39bd 100644
--- a/charger/vaillant.go
+++ b/charger/vaillant.go
@@ -26,7 +26,7 @@ import (
 
 	"github.com/WulfgarW/sensonet"
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/samber/lo"
@@ -127,7 +127,7 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]interface{}) (a
 
 	var power func() (float64, error)
 	if devices, _ := conn.GetMpcData(systemId); len(devices) > 0 {
-		power = provider.Cached(func() (float64, error) {
+		power = plugin.Cached(func() (float64, error) {
 			res, err := conn.GetMpcData(systemId)
 			return lo.SumBy(res, func(d sensonet.MpcDevice) float64 {
 				return d.CurrentPower
@@ -153,7 +153,7 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]interface{}) (a
 
 	var temp func() (float64, error)
 	if !heating || heatingTempSensor {
-		temp = provider.Cached(func() (float64, error) {
+		temp = plugin.Cached(func() (float64, error) {
 			system, err := conn.GetSystem(systemId)
 			if err != nil {
 				return 0, err
diff --git a/charger/warp2.go b/charger/warp2.go
index a477a3ab..d46c2007 100644
--- a/charger/warp2.go
+++ b/charger/warp2.go
@@ -9,8 +9,8 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/warp"
-	"github.com/evcc-io/evcc/provider"
-	"github.com/evcc-io/evcc/provider/mqtt"
+	"github.com/evcc-io/evcc/plugin"
+	"github.com/evcc-io/evcc/plugin/mqtt"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -104,14 +104,14 @@ func NewWarp2(mqttconf mqtt.Config, topic, emTopic string, timeout time.Duration
 	}
 
 	// timeout handler
-	h, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/evse/low_level_state", topic), timeout).StringGetter()
+	h, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/evse/low_level_state", topic), timeout).StringGetter()
 	if err != nil {
 		return nil, err
 	}
-	to := provider.NewTimeoutHandler(h)
+	to := plugin.NewTimeoutHandler(h)
 
-	mq := func(s string, args ...any) *provider.Mqtt {
-		return provider.NewMqtt(log, client, fmt.Sprintf(s, args...), 0)
+	mq := func(s string, args ...any) *plugin.Mqtt {
+		return plugin.NewMqtt(log, client, fmt.Sprintf(s, args...), 0)
 	}
 
 	wb.maxcurrentG, err = to.JsonGetter(mq("%s/evse/external_current", topic))
@@ -135,7 +135,7 @@ func NewWarp2(mqttconf mqtt.Config, topic, emTopic string, timeout time.Duration
 		return nil, err
 	}
 
-	wb.maxcurrentS, err = provider.NewMqtt(log, client,
+	wb.maxcurrentS, err = plugin.NewMqtt(log, client,
 		fmt.Sprintf("%s/evse/external_current_update", topic), 0).
 		WithPayload(`{ "current": ${maxcurrent} }`).
 		IntSetter("maxcurrent")
@@ -148,7 +148,7 @@ func NewWarp2(mqttconf mqtt.Config, topic, emTopic string, timeout time.Duration
 		return nil, err
 	}
 
-	wb.phasesS, err = provider.NewMqtt(log, client,
+	wb.phasesS, err = plugin.NewMqtt(log, client,
 		fmt.Sprintf("%s/power_manager/external_control_update", emTopic), 0).
 		WithPayload(`{ "phases_wanted": ${phases} }`).
 		IntSetter("phases")
@@ -171,7 +171,7 @@ func (wb *Warp2) hasFeature(root, feature string, timeout time.Duration) bool {
 
 	topic := fmt.Sprintf("%s/info/features", root)
 
-	if dataG, err := provider.NewMqtt(wb.log, wb.client, topic, timeout).StringGetter(); err == nil {
+	if dataG, err := plugin.NewMqtt(wb.log, wb.client, topic, timeout).StringGetter(); err == nil {
 		if data, err := dataG(); err == nil {
 			if err := json.Unmarshal([]byte(data), &wb.features); err == nil {
 				return slices.Contains(wb.features, feature)
diff --git a/charger/zaptec.go b/charger/zaptec.go
index fddcf0b4..871154f3 100644
--- a/charger/zaptec.go
+++ b/charger/zaptec.go
@@ -28,7 +28,7 @@ import (
 	"github.com/coreos/go-oidc/v3/oidc"
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/zaptec"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/sponsor"
@@ -43,7 +43,7 @@ import (
 type Zaptec struct {
 	*request.Helper
 	log      *util.Logger
-	statusG  provider.Cacheable[zaptec.StateResponse]
+	statusG  plugin.Cacheable[zaptec.StateResponse]
 	id       string
 	enabled  bool
 	priority bool
@@ -91,7 +91,7 @@ func NewZaptec(user, password, id string, priority bool, cache time.Duration) (a
 	}
 
 	// setup cached values
-	c.statusG = provider.ResettableCached(func() (zaptec.StateResponse, error) {
+	c.statusG = plugin.ResettableCached(func() (zaptec.StateResponse, error) {
 		var res zaptec.StateResponse
 
 		uri := fmt.Sprintf("%s/api/chargers/%s/state", zaptec.ApiURL, c.id)
diff --git a/cmd/configure/helper.go b/cmd/configure/helper.go
index 8e2e72ac..419f3a22 100644
--- a/cmd/configure/helper.go
+++ b/cmd/configure/helper.go
@@ -6,7 +6,7 @@ import (
 	"sort"
 	"strings"
 
-	"github.com/evcc-io/evcc/provider/mqtt"
+	"github.com/evcc-io/evcc/plugin/mqtt"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/sponsor"
 	"github.com/evcc-io/evcc/util/templates"
diff --git a/cmd/setup.go b/cmd/setup.go
index 9733e0e8..6194951e 100644
--- a/cmd/setup.go
+++ b/cmd/setup.go
@@ -24,9 +24,9 @@ import (
 	"github.com/evcc-io/evcc/core/keys"
 	"github.com/evcc-io/evcc/hems"
 	"github.com/evcc-io/evcc/meter"
-	"github.com/evcc-io/evcc/provider/golang"
-	"github.com/evcc-io/evcc/provider/javascript"
-	"github.com/evcc-io/evcc/provider/mqtt"
+	"github.com/evcc-io/evcc/plugin/golang"
+	"github.com/evcc-io/evcc/plugin/javascript"
+	"github.com/evcc-io/evcc/plugin/mqtt"
 	"github.com/evcc-io/evcc/push"
 	"github.com/evcc-io/evcc/server"
 	"github.com/evcc-io/evcc/server/db"
diff --git a/core/circuit/circuit.go b/core/circuit/circuit.go
index d552caa2..4acd1fa9 100644
--- a/core/circuit/circuit.go
+++ b/core/circuit/circuit.go
@@ -8,7 +8,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/config"
 )
@@ -41,14 +41,14 @@ type Circuit struct {
 // NewFromConfig creates a new Circuit
 func NewFromConfig(log *util.Logger, other map[string]interface{}) (api.Circuit, error) {
 	cc := struct {
-		Title         string           // title
-		ParentRef     string           `mapstructure:"parent"` // parent circuit reference
-		MeterRef      string           `mapstructure:"meter"`  // meter reference
-		MaxCurrent    float64          // the max allowed current
-		MaxPower      float64          // the max allowed power
-		GetMaxCurrent *provider.Config // dynamic max allowed current
-		GetMaxPower   *provider.Config // dynamic max allowed power
-		Timeout       time.Duration    // timeout between meter updates
+		Title         string         // title
+		ParentRef     string         `mapstructure:"parent"` // parent circuit reference
+		MeterRef      string         `mapstructure:"meter"`  // meter reference
+		MaxCurrent    float64        // the max allowed current
+		MaxPower      float64        // the max allowed power
+		GetMaxCurrent *plugin.Config // dynamic max allowed current
+		GetMaxPower   *plugin.Config // dynamic max allowed power
+		Timeout       time.Duration  // timeout between meter updates
 	}{
 		Timeout: time.Minute,
 	}
@@ -71,20 +71,14 @@ func NewFromConfig(log *util.Logger, other map[string]interface{}) (api.Circuit,
 		return nil, err
 	}
 
-	if cc.GetMaxPower != nil {
-		res, err := provider.NewFloatGetterFromConfig(context.TODO(), *cc.GetMaxPower)
-		if err != nil {
-			return nil, err
-		}
-		circuit.getMaxPower = res
+	circuit.getMaxPower, err = cc.GetMaxPower.FloatGetter(context.TODO())
+	if err != nil {
+		return nil, err
 	}
 
-	if cc.GetMaxCurrent != nil {
-		res, err := provider.NewFloatGetterFromConfig(context.TODO(), *cc.GetMaxCurrent)
-		if err != nil {
-			return nil, err
-		}
-		circuit.getMaxCurrent = res
+	circuit.getMaxCurrent, err = cc.GetMaxCurrent.FloatGetter(context.TODO())
+	if err != nil {
+		return nil, err
 	}
 
 	if cc.ParentRef != "" {
diff --git a/core/loadpoint.go b/core/loadpoint.go
index 1dc72aec..bca0447b 100644
--- a/core/loadpoint.go
+++ b/core/loadpoint.go
@@ -23,7 +23,7 @@ import (
 	"github.com/evcc-io/evcc/core/soc"
 	"github.com/evcc-io/evcc/core/vehicle"
 	"github.com/evcc-io/evcc/core/wrapper"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/push"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/config"
@@ -457,7 +457,7 @@ func (lp *Loadpoint) evChargeStopHandler() {
 	}
 
 	// soc update reset
-	provider.ResetCached()
+	plugin.ResetCached()
 	lp.socUpdated = time.Time{}
 
 	// reset pv enable/disable timer
diff --git a/core/loadpoint_vehicle.go b/core/loadpoint_vehicle.go
index d758a3f8..2b280e1a 100644
--- a/core/loadpoint_vehicle.go
+++ b/core/loadpoint_vehicle.go
@@ -13,7 +13,7 @@ import (
 	"github.com/evcc-io/evcc/core/session"
 	"github.com/evcc-io/evcc/core/soc"
 	"github.com/evcc-io/evcc/core/vehicle"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 const (
@@ -234,7 +234,7 @@ func (lp *Loadpoint) vehicleUnidentified() bool {
 	select {
 	case <-lp.vehicleDetectTicker.C:
 		lp.log.DEBUG.Println("vehicle api refresh")
-		provider.ResetCached()
+		plugin.ResetCached()
 	default:
 	}
 
@@ -260,7 +260,7 @@ func (lp *Loadpoint) vehicleDefaultOrDetect() {
 func (lp *Loadpoint) startVehicleDetection() {
 	// flush all vehicles before detection starts
 	lp.log.DEBUG.Println("vehicle api refresh")
-	provider.ResetCached()
+	plugin.ResetCached()
 
 	lp.vehicleDetect = lp.clock.Now()
 	lp.vehicleDetectTicker = lp.clock.Ticker(vehicleDetectInterval)
diff --git a/hems/eebus/eebus.go b/hems/eebus/eebus.go
index 629dff1d..21eebe39 100644
--- a/hems/eebus/eebus.go
+++ b/hems/eebus/eebus.go
@@ -9,7 +9,7 @@ import (
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/core/circuit"
 	"github.com/evcc-io/evcc/core/site"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/server/eebus"
 	"github.com/evcc-io/evcc/util"
 )
@@ -30,7 +30,7 @@ type EEBus struct {
 	failsafeLimit    float64
 	failsafeDuration time.Duration
 
-	heartbeat *provider.Value[struct{}]
+	heartbeat *plugin.Value[struct{}]
 }
 
 type Limits struct {
@@ -90,7 +90,7 @@ func NewEEBus(ski string, limits Limits, root api.Circuit) (*EEBus, error) {
 		root:      root,
 		uc:        eebus.Instance.ControllableSystem(),
 		Connector: eebus.NewConnector(),
-		heartbeat: provider.NewValue[struct{}](2 * time.Minute), // LPC-031
+		heartbeat: plugin.NewValue[struct{}](2 * time.Minute), // LPC-031
 
 		consumptionLimit: &ucapi.LoadLimit{
 			Value:        limits.ConsumptionLimit,
diff --git a/hems/relay/relay.go b/hems/relay/relay.go
index 2d8e408d..07d17f05 100644
--- a/hems/relay/relay.go
+++ b/hems/relay/relay.go
@@ -8,7 +8,7 @@ import (
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/core/circuit"
 	"github.com/evcc-io/evcc/core/site"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -24,7 +24,7 @@ type Relay struct {
 func New(ctx context.Context, other map[string]interface{}, site site.API) (*Relay, error) {
 	var cc struct {
 		MaxPower float64
-		Limit    provider.Config
+		Limit    plugin.Config
 	}
 
 	if err := util.DecodeOther(other, &cc); err != nil {
@@ -50,7 +50,7 @@ func New(ctx context.Context, other map[string]interface{}, site site.API) (*Rel
 	site.SetCircuit(lpc)
 
 	// limit getter
-	limitG, err := provider.NewBoolGetterFromConfig(ctx, cc.Limit)
+	limitG, err := cc.Limit.BoolGetter(ctx)
 	if err != nil {
 		return nil, err
 	}
diff --git a/meter/discovergy.go b/meter/discovergy.go
index 700f8e72..0a310179 100644
--- a/meter/discovergy.go
+++ b/meter/discovergy.go
@@ -6,7 +6,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/meter/discovergy"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/transport"
@@ -74,7 +74,7 @@ func NewDiscovergyFromConfig(other map[string]interface{}) (api.Meter, error) {
 		}))
 	}
 
-	dataG := provider.Cached(func() (discovergy.Reading, error) {
+	dataG := plugin.Cached(func() (discovergy.Reading, error) {
 		var res discovergy.Reading
 		uri := fmt.Sprintf("%s/last_reading?meterId=%s", discovergy.API, meterID)
 		err := client.GetJSON(uri, &res)
diff --git a/meter/eebus.go b/meter/eebus.go
index 3a9ca4b4..fdd54068 100644
--- a/meter/eebus.go
+++ b/meter/eebus.go
@@ -8,7 +8,7 @@ import (
 	"github.com/enbility/eebus-go/usecases/ma/mgcp"
 	spineapi "github.com/enbility/spine-go/api"
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/server/eebus"
 	"github.com/evcc-io/evcc/util"
 )
@@ -19,8 +19,8 @@ type EEBus struct {
 	*eebus.Connector
 	uc *eebus.UseCasesCS
 
-	power, energy      *provider.Value[float64]
-	voltages, currents *provider.Value[[]float64]
+	power, energy      *plugin.Value[float64]
+	voltages, currents *plugin.Value[[]float64]
 }
 
 func init() {
@@ -54,10 +54,10 @@ func NewEEBus(ski, ip string, timeout time.Duration) (*EEBus, error) {
 		log:       util.NewLogger("eebus"),
 		uc:        eebus.Instance.ControllableSystem(),
 		Connector: eebus.NewConnector(),
-		power:     provider.NewValue[float64](timeout),
-		energy:    provider.NewValue[float64](timeout),
-		voltages:  provider.NewValue[[]float64](timeout),
-		currents:  provider.NewValue[[]float64](timeout),
+		power:     plugin.NewValue[float64](timeout),
+		energy:    plugin.NewValue[float64](timeout),
+		voltages:  plugin.NewValue[[]float64](timeout),
+		currents:  plugin.NewValue[[]float64](timeout),
 	}
 
 	if err := eebus.Instance.RegisterDevice(ski, ip, c); err != nil {
diff --git a/meter/helper.go b/meter/helper.go
index 9f859215..bcfa9685 100644
--- a/meter/helper.go
+++ b/meter/helper.go
@@ -5,34 +5,26 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 // BuildMeasurements returns typical meter measurement getters from config
-func BuildMeasurements(ctx context.Context, power, energy *provider.Config) (func() (float64, error), func() (float64, error), error) {
-	var powerG func() (float64, error)
-	if power != nil {
-		var err error
-		powerG, err = provider.NewFloatGetterFromConfig(ctx, *power)
-		if err != nil {
-			return nil, nil, fmt.Errorf("power: %w", err)
-		}
+func BuildMeasurements(ctx context.Context, power, energy *plugin.Config) (func() (float64, error), func() (float64, error), error) {
+	powerG, err := power.FloatGetter(ctx)
+	if err != nil {
+		return nil, nil, fmt.Errorf("power: %w", err)
 	}
 
-	var energyG func() (float64, error)
-	if energy != nil {
-		var err error
-		energyG, err = provider.NewFloatGetterFromConfig(ctx, *energy)
-		if err != nil {
-			return nil, nil, fmt.Errorf("energy: %w", err)
-		}
+	energyG, err := energy.FloatGetter(ctx)
+	if err != nil {
+		return nil, nil, fmt.Errorf("energy: %w", err)
 	}
 
 	return powerG, energyG, nil
 }
 
 // BuildPhaseMeasurements returns typical meter measurement getters from config
-func BuildPhaseMeasurements(ctx context.Context, currents, voltages, powers []provider.Config) (
+func BuildPhaseMeasurements(ctx context.Context, currents, voltages, powers []plugin.Config) (
 	func() (float64, float64, float64, error),
 	func() (float64, float64, float64, error),
 	func() (float64, float64, float64, error),
@@ -57,7 +49,7 @@ func BuildPhaseMeasurements(ctx context.Context, currents, voltages, powers []pr
 }
 
 // buildPhaseProviders returns phases getter for given config
-func buildPhaseProviders(ctx context.Context, providers []provider.Config) (func() (float64, float64, float64, error), error) {
+func buildPhaseProviders(ctx context.Context, providers []plugin.Config) (func() (float64, float64, float64, error), error) {
 	if len(providers) == 0 {
 		return nil, nil
 	}
@@ -68,7 +60,7 @@ func buildPhaseProviders(ctx context.Context, providers []provider.Config) (func
 
 	var phases [3]func() (float64, error)
 	for idx, prov := range providers {
-		c, err := provider.NewFloatGetterFromConfig(ctx, prov)
+		c, err := prov.FloatGetter(ctx)
 		if err != nil {
 			return nil, fmt.Errorf("[%d] %w", idx, err)
 		}
diff --git a/meter/homematic/connection.go b/meter/homematic/connection.go
index c21865be..ce7ae0ac 100644
--- a/meter/homematic/connection.go
+++ b/meter/homematic/connection.go
@@ -8,7 +8,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/transport"
@@ -29,8 +29,8 @@ type Connection struct {
 	log *util.Logger
 	*request.Helper
 	*Settings
-	meterG  provider.Cacheable[MethodResponse]
-	switchG provider.Cacheable[MethodResponse]
+	meterG  plugin.Cacheable[MethodResponse]
+	switchG plugin.Cacheable[MethodResponse]
 }
 
 // NewConnection creates a new Homematic device connection.
@@ -58,11 +58,11 @@ func NewConnection(uri, device, meterchannel, switchchannel, user, password stri
 		conn.Client.Transport = transport.BasicAuth(user, password, conn.Client.Transport)
 	}
 
-	conn.switchG = provider.ResettableCached(func() (MethodResponse, error) {
+	conn.switchG = plugin.ResettableCached(func() (MethodResponse, error) {
 		return conn.XmlCmd("getParamset", conn.SwitchChannel, Param{CCUString: "VALUES"})
 	}, conn.Cache)
 
-	conn.meterG = provider.ResettableCached(func() (MethodResponse, error) {
+	conn.meterG = plugin.ResettableCached(func() (MethodResponse, error) {
 		return conn.XmlCmd("getParamset", conn.MeterChannel, Param{CCUString: "VALUES"})
 	}, conn.Cache)
 
diff --git a/meter/homewizard/connection.go b/meter/homewizard/connection.go
index 6aa3d315..db5c2d9c 100644
--- a/meter/homewizard/connection.go
+++ b/meter/homewizard/connection.go
@@ -7,7 +7,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/transport"
@@ -19,8 +19,8 @@ type Connection struct {
 	uri         string
 	usage       string
 	ProductType string
-	dataG       provider.Cacheable[DataResponse]
-	stateG      provider.Cacheable[StateResponse]
+	dataG       plugin.Cacheable[DataResponse]
+	stateG      plugin.Cacheable[StateResponse]
 }
 
 // NewConnection creates a homewizard connection
@@ -50,13 +50,13 @@ func NewConnection(uri string, usage string, cache time.Duration) (*Connection,
 	c.uri = c.uri + "/" + res.ApiVersion
 	c.ProductType = res.ProductType
 
-	c.dataG = provider.ResettableCached(func() (DataResponse, error) {
+	c.dataG = plugin.ResettableCached(func() (DataResponse, error) {
 		var res DataResponse
 		err := c.GetJSON(fmt.Sprintf("%s/data", c.uri), &res)
 		return res, err
 	}, cache)
 
-	c.stateG = provider.ResettableCached(func() (StateResponse, error) {
+	c.stateG = plugin.ResettableCached(func() (StateResponse, error) {
 		var res StateResponse
 		err := c.GetJSON(fmt.Sprintf("%s/state", c.uri), &res)
 		return res, err
diff --git a/meter/lgpcs/lgpcs.go b/meter/lgpcs/lgpcs.go
index 2fa571f6..00e5de06 100644
--- a/meter/lgpcs/lgpcs.go
+++ b/meter/lgpcs/lgpcs.go
@@ -10,7 +10,7 @@ import (
 	"sync"
 	"time"
 
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/transport"
@@ -63,7 +63,7 @@ func GetInstance(uri, registration, password string, cache time.Duration, essTyp
 
 		// caches the data access for the "cache" time duration
 		// sends a new request to the pcs if the cache is expired and Data() requested
-		instance.dataG = provider.Cached(instance.refreshData, cache)
+		instance.dataG = plugin.Cached(instance.refreshData, cache)
 
 		// do first login if no authKey exists and uri and password exist
 		if instance.authKey == "" && instance.uri != "" && instance.password != "" {
diff --git a/meter/meter.go b/meter/meter.go
index c24a77df..4c669f83 100644
--- a/meter/meter.go
+++ b/meter/meter.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -18,19 +18,19 @@ func init() {
 // NewConfigurableFromConfig creates api.Meter from config
 func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}) (api.Meter, error) {
 	cc := struct {
-		Power    provider.Config
-		Energy   *provider.Config  // optional
-		Currents []provider.Config // optional
-		Voltages []provider.Config // optional
-		Powers   []provider.Config // optional
+		Power    plugin.Config
+		Energy   *plugin.Config  // optional
+		Currents []plugin.Config // optional
+		Voltages []plugin.Config // optional
+		Powers   []plugin.Config // optional
 
 		// battery
 		capacity    `mapstructure:",squash"`
 		maxpower    `mapstructure:",squash"`
 		battery     `mapstructure:",squash"`
-		Soc         *provider.Config // optional
-		LimitSoc    *provider.Config // optional
-		BatteryMode *provider.Config // optional
+		Soc         *plugin.Config // optional
+		LimitSoc    *plugin.Config // optional
+		BatteryMode *plugin.Config // optional
 	}{
 		battery: battery{
 			MinSoc: 20,
@@ -55,19 +55,16 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 	m, _ := NewConfigurable(powerG)
 
 	// decorate soc
-	var socG func() (float64, error)
-	if cc.Soc != nil {
-		socG, err = provider.NewFloatGetterFromConfig(ctx, *cc.Soc)
-		if err != nil {
-			return nil, fmt.Errorf("battery soc: %w", err)
-		}
+	socG, err := cc.Soc.FloatGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("battery soc: %w", err)
 	}
 
 	var batModeS func(api.BatteryMode) error
 
 	switch {
 	case cc.Soc != nil && cc.LimitSoc != nil:
-		limitSocS, err := provider.NewFloatSetterFromConfig(ctx, "limitSoc", *cc.LimitSoc)
+		limitSocS, err := cc.LimitSoc.FloatSetter(ctx, "limitSoc")
 		if err != nil {
 			return nil, fmt.Errorf("battery limit soc: %w", err)
 		}
@@ -75,7 +72,7 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 		batModeS = cc.battery.LimitController(socG, limitSocS)
 
 	case cc.BatteryMode != nil:
-		modeS, err := provider.NewIntSetterFromConfig(ctx, "batteryMode", *cc.BatteryMode)
+		modeS, err := cc.BatteryMode.IntSetter(ctx, "batteryMode")
 		if err != nil {
 			return nil, fmt.Errorf("battery mode: %w", err)
 		}
diff --git a/meter/openwb.go b/meter/openwb.go
index 371a876a..c5f731c5 100644
--- a/meter/openwb.go
+++ b/meter/openwb.go
@@ -8,8 +8,8 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/openwb"
-	"github.com/evcc-io/evcc/provider"
-	"github.com/evcc-io/evcc/provider/mqtt"
+	"github.com/evcc-io/evcc/plugin"
+	"github.com/evcc-io/evcc/plugin/mqtt"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -42,14 +42,14 @@ func NewOpenWBFromConfig(other map[string]interface{}) (api.Meter, error) {
 	}
 
 	// timeout handler
-	h, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/system/%s", cc.Topic, openwb.TimestampTopic), cc.Timeout).StringGetter()
+	h, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/system/%s", cc.Topic, openwb.TimestampTopic), cc.Timeout).StringGetter()
 	if err != nil {
 		return nil, err
 	}
-	to := provider.NewTimeoutHandler(h)
+	to := plugin.NewTimeoutHandler(h)
 
-	mq := func(s string, args ...any) *provider.Mqtt {
-		return provider.NewMqtt(log, client, fmt.Sprintf(s, args...), 0)
+	mq := func(s string, args ...any) *plugin.Mqtt {
+		return plugin.NewMqtt(log, client, fmt.Sprintf(s, args...), 0)
 	}
 
 	var power func() (float64, error)
@@ -77,7 +77,7 @@ func NewOpenWBFromConfig(other map[string]interface{}) (api.Meter, error) {
 
 	case "pv":
 		// first pv
-		configuredG, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/pv/1/%s", cc.Topic, openwb.PvConfigured), cc.Timeout).BoolGetter()
+		configuredG, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/pv/1/%s", cc.Topic, openwb.PvConfigured), cc.Timeout).BoolGetter()
 		if err != nil {
 			return nil, err
 		}
@@ -100,7 +100,7 @@ func NewOpenWBFromConfig(other map[string]interface{}) (api.Meter, error) {
 		}
 
 	case "battery":
-		configuredG, err := provider.NewMqtt(log, client, fmt.Sprintf("%s/housebattery/%s", cc.Topic, openwb.BatteryConfigured), cc.Timeout).BoolGetter()
+		configuredG, err := plugin.NewMqtt(log, client, fmt.Sprintf("%s/housebattery/%s", cc.Topic, openwb.BatteryConfigured), cc.Timeout).BoolGetter()
 		if err != nil {
 			return nil, err
 		}
diff --git a/meter/powerwall.go b/meter/powerwall.go
index 022fc020..f45a821b 100644
--- a/meter/powerwall.go
+++ b/meter/powerwall.go
@@ -13,7 +13,7 @@ import (
 	"github.com/andig/go-powerwall"
 	"github.com/bogosj/tesla"
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"golang.org/x/oauth2"
@@ -90,7 +90,7 @@ func NewPowerWall(uri, usage, user, password string, cache time.Duration, refres
 	m := &PowerWall{
 		client: client,
 		usage:  strings.ToLower(usage),
-		meterG: provider.Cached(client.GetMetersAggregates, cache),
+		meterG: plugin.Cached(client.GetMetersAggregates, cache),
 	}
 
 	var batteryControl bool
diff --git a/meter/sma.go b/meter/sma.go
index 7715f22f..b715061a 100644
--- a/meter/sma.go
+++ b/meter/sma.go
@@ -8,7 +8,7 @@ import (
 	"text/tabwriter"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider/sma"
+	"github.com/evcc-io/evcc/plugin/sma"
 	"github.com/evcc-io/evcc/util"
 	"gitlab.com/bboehmke/sunny"
 )
diff --git a/meter/tasmota/connection.go b/meter/tasmota/connection.go
index 367b1a3e..d252edc6 100644
--- a/meter/tasmota/connection.go
+++ b/meter/tasmota/connection.go
@@ -7,7 +7,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/transport"
@@ -18,8 +18,8 @@ type Connection struct {
 	*request.Helper
 	uri, user, password string
 	channels            []int
-	statusSnsG          provider.Cacheable[StatusSNSResponse]
-	statusStsG          provider.Cacheable[StatusSTSResponse]
+	statusSnsG          plugin.Cacheable[StatusSNSResponse]
+	statusStsG          plugin.Cacheable[StatusSTSResponse]
 }
 
 // NewConnection creates a Tasmota connection
@@ -50,7 +50,7 @@ func NewConnection(uri, user, password string, channels []int, cache time.Durati
 
 	c.Client.Transport = request.NewTripper(log, transport.Insecure())
 
-	c.statusSnsG = provider.ResettableCached(func() (StatusSNSResponse, error) {
+	c.statusSnsG = plugin.ResettableCached(func() (StatusSNSResponse, error) {
 		parameters := url.Values{
 			"user":     {c.user},
 			"password": {c.password},
@@ -61,7 +61,7 @@ func NewConnection(uri, user, password string, channels []int, cache time.Durati
 		return res, err
 	}, cache)
 
-	c.statusStsG = provider.ResettableCached(func() (StatusSTSResponse, error) {
+	c.statusStsG = plugin.ResettableCached(func() (StatusSTSResponse, error) {
 		parameters := url.Values{
 			"user":     {c.user},
 			"password": {c.password},
diff --git a/meter/tq-em.go b/meter/tq-em.go
index 214aa9a1..000829b6 100644
--- a/meter/tq-em.go
+++ b/meter/tq-em.go
@@ -10,7 +10,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 )
@@ -91,7 +91,7 @@ func NewTqEmFromConfig(other map[string]interface{}) (api.Meter, error) {
 		return nil, errors.New("no serial")
 	}
 
-	dataG := provider.Cached(func() (tqemData, error) {
+	dataG := plugin.Cached(func() (tqemData, error) {
 		var res tqemData
 
 		uri := fmt.Sprintf("%s/mum-webservice/data.php", base)
diff --git a/meter/tq-em420.go b/meter/tq-em420.go
index d445ec6f..71788d52 100644
--- a/meter/tq-em420.go
+++ b/meter/tq-em420.go
@@ -8,7 +8,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 )
@@ -115,7 +115,7 @@ func NewTqEm420FromConfig(other map[string]interface{}) (api.Meter, error) {
 
 	base := util.DefaultScheme(strings.TrimRight(cc.URI, "/"), "http")
 
-	dataG := provider.Cached(func() (TqEm420Data, error) {
+	dataG := plugin.Cached(func() (TqEm420Data, error) {
 		var res TqEm420Data
 
 		headers := map[string]string{
diff --git a/meter/zendure/connection.go b/meter/zendure/connection.go
index f1c7de9f..4fcf8c3a 100644
--- a/meter/zendure/connection.go
+++ b/meter/zendure/connection.go
@@ -8,7 +8,7 @@ import (
 	"time"
 
 	"dario.cat/mergo"
-	"github.com/evcc-io/evcc/provider/mqtt"
+	"github.com/evcc-io/evcc/plugin/mqtt"
 	"github.com/evcc-io/evcc/util"
 )
 
diff --git a/provider/cache.go b/plugin/cache.go
similarity index 99%
rename from provider/cache.go
rename to plugin/cache.go
index 081903ac..0d1da3a1 100644
--- a/provider/cache.go
+++ b/plugin/cache.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"errors"
diff --git a/provider/calc.go b/plugin/calc.go
similarity index 71%
rename from provider/calc.go
rename to plugin/calc.go
index fe1fb284..660c6cdc 100644
--- a/provider/calc.go
+++ b/plugin/calc.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -9,7 +9,7 @@ import (
 	"github.com/evcc-io/evcc/util"
 )
 
-type calcProvider struct {
+type calcPlugin struct {
 	add, mul, div []func() (float64, error)
 	abs, sign     func() (float64, error)
 }
@@ -19,7 +19,7 @@ func init() {
 }
 
 // NewCalcFromConfig creates calc provider
-func NewCalcFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewCalcFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Add  []Config
 		Mul  []Config
@@ -43,10 +43,10 @@ func NewCalcFromConfig(ctx context.Context, other map[string]interface{}) (Provi
 		return nil, errors.New("can only have either add, mul, div, abs or sign")
 	}
 
-	o := &calcProvider{}
+	o := new(calcPlugin)
 
 	for idx, cc := range cc.Add {
-		f, err := NewFloatGetterFromConfig(ctx, cc)
+		f, err := cc.FloatGetter(ctx)
 		if err != nil {
 			return nil, fmt.Errorf("add[%d]: %w", idx, err)
 		}
@@ -54,7 +54,7 @@ func NewCalcFromConfig(ctx context.Context, other map[string]interface{}) (Provi
 	}
 
 	for idx, cc := range cc.Mul {
-		f, err := NewFloatGetterFromConfig(ctx, cc)
+		f, err := cc.FloatGetter(ctx)
 		if err != nil {
 			return nil, fmt.Errorf("mul[%d]: %w", idx, err)
 		}
@@ -62,57 +62,53 @@ func NewCalcFromConfig(ctx context.Context, other map[string]interface{}) (Provi
 	}
 
 	for idx, cc := range cc.Div {
-		f, err := NewFloatGetterFromConfig(ctx, cc)
+		f, err := cc.FloatGetter(ctx)
 		if err != nil {
 			return nil, fmt.Errorf("div[%d]: %w", idx, err)
 		}
 		o.div = append(o.div, f)
 	}
 
-	if cc.Abs != nil {
-		f, err := NewFloatGetterFromConfig(ctx, *cc.Abs)
-		if err != nil {
-			return nil, fmt.Errorf("abs: %w", err)
-		}
-		o.abs = f
+	var err error
+
+	o.abs, err = cc.Abs.FloatGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("abs: %w", err)
 	}
 
-	if cc.Sign != nil {
-		f, err := NewFloatGetterFromConfig(ctx, *cc.Sign)
-		if err != nil {
-			return nil, fmt.Errorf("sign: %w", err)
-		}
-		o.sign = f
+	o.sign, err = cc.Sign.FloatGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("sign: %w", err)
 	}
 
 	return o, nil
 }
 
-var _ IntProvider = (*calcProvider)(nil)
+var _ IntGetter = (*calcPlugin)(nil)
 
-func (o *calcProvider) IntGetter() (func() (int64, error), error) {
+func (o *calcPlugin) IntGetter() (func() (int64, error), error) {
 	return func() (int64, error) {
 		f, err := o.floatGetter()
 		return int64(f), err
 	}, nil
 }
 
-var _ StringProvider = (*calcProvider)(nil)
+var _ StringGetter = (*calcPlugin)(nil)
 
-func (o *calcProvider) StringGetter() (func() (string, error), error) {
+func (o *calcPlugin) StringGetter() (func() (string, error), error) {
 	return func() (string, error) {
 		f, err := o.floatGetter()
 		return fmt.Sprintf("%c", int(f)), err
 	}, nil
 }
 
-var _ FloatProvider = (*calcProvider)(nil)
+var _ FloatGetter = (*calcPlugin)(nil)
 
-func (o *calcProvider) FloatGetter() (func() (float64, error), error) {
+func (o *calcPlugin) FloatGetter() (func() (float64, error), error) {
 	return o.floatGetter, nil
 }
 
-func (o *calcProvider) floatGetter() (float64, error) {
+func (o *calcPlugin) floatGetter() (float64, error) {
 	var res float64
 
 	switch {
diff --git a/provider/charger.go b/plugin/charger.go
similarity index 60%
rename from provider/charger.go
rename to plugin/charger.go
index 6696f331..07e2f4a3 100644
--- a/provider/charger.go
+++ b/plugin/charger.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -10,7 +10,7 @@ import (
 	"github.com/spf13/cast"
 )
 
-type switchChargerProvider struct {
+type switchChargerPlugin struct {
 	charger api.Charger
 }
 
@@ -19,7 +19,7 @@ func init() {
 }
 
 // NewChargerEnableFromConfig creates type conversion provider
-func NewChargerEnableFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewChargerEnableFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Config config.Typed
 	}
@@ -33,34 +33,34 @@ func NewChargerEnableFromConfig(ctx context.Context, other map[string]interface{
 		return nil, err
 	}
 
-	o := &switchChargerProvider{
+	o := &switchChargerPlugin{
 		charger: charger,
 	}
 
 	return o, nil
 }
 
-var _ FloatProvider = (*switchChargerProvider)(nil)
+var _ FloatGetter = (*switchChargerPlugin)(nil)
 
-func (o *switchChargerProvider) FloatGetter() (func() (float64, error), error) {
+func (o *switchChargerPlugin) FloatGetter() (func() (float64, error), error) {
 	return func() (float64, error) {
 		v, err := o.charger.Enabled()
 		return cast.ToFloat64(v), err
 	}, nil
 }
 
-var _ IntProvider = (*switchChargerProvider)(nil)
+var _ IntGetter = (*switchChargerPlugin)(nil)
 
-func (o *switchChargerProvider) IntGetter() (func() (int64, error), error) {
+func (o *switchChargerPlugin) IntGetter() (func() (int64, error), error) {
 	return func() (int64, error) {
 		v, err := o.charger.Enabled()
 		return cast.ToInt64(v), err
 	}, nil
 }
 
-var _ SetIntProvider = (*switchChargerProvider)(nil)
+var _ IntSetter = (*switchChargerPlugin)(nil)
 
-func (o *switchChargerProvider) IntSetter(param string) (func(int64) error, error) {
+func (o *switchChargerPlugin) IntSetter(param string) (func(int64) error, error) {
 	return func(val int64) error {
 		b, err := cast.ToBoolE(val)
 		if err != nil {
@@ -70,17 +70,17 @@ func (o *switchChargerProvider) IntSetter(param string) (func(int64) error, erro
 	}, nil
 }
 
-var _ BoolProvider = (*switchChargerProvider)(nil)
+var _ BoolGetter = (*switchChargerPlugin)(nil)
 
-func (o *switchChargerProvider) BoolGetter() (func() (bool, error), error) {
+func (o *switchChargerPlugin) BoolGetter() (func() (bool, error), error) {
 	return func() (bool, error) {
 		return o.charger.Enabled()
 	}, nil
 }
 
-var _ SetBoolProvider = (*switchChargerProvider)(nil)
+var _ BoolSetter = (*switchChargerPlugin)(nil)
 
-func (o *switchChargerProvider) BoolSetter(param string) (func(bool) error, error) {
+func (o *switchChargerPlugin) BoolSetter(param string) (func(bool) error, error) {
 	return func(val bool) error {
 		return o.charger.Enable(val)
 	}, nil
diff --git a/provider/combined.go b/plugin/combined.go
similarity index 63%
rename from provider/combined.go
rename to plugin/combined.go
index d9ea83df..c43b111e 100644
--- a/provider/combined.go
+++ b/plugin/combined.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -13,13 +13,13 @@ func init() {
 	registry.AddCtx("openwb", NewCombinedFromConfig)
 }
 
-// combinedProvider implements status conversion from openWB to api.Status
-type combinedProvider struct {
+// combinedPlugin implements status conversion from openWB to api.Status
+type combinedPlugin struct {
 	plugged, charging func() (bool, error)
 }
 
 // NewCombinedFromConfig creates combined provider
-func NewCombinedFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewCombinedFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Plugged, Charging Config
 	}
@@ -28,33 +28,33 @@ func NewCombinedFromConfig(ctx context.Context, other map[string]interface{}) (P
 		return nil, err
 	}
 
-	plugged, err := NewBoolGetterFromConfig(ctx, cc.Plugged)
+	plugged, err := cc.Plugged.BoolGetter(ctx)
 	if err != nil {
 		return nil, fmt.Errorf("plugged: %w", err)
 	}
 
-	charging, err := NewBoolGetterFromConfig(ctx, cc.Charging)
+	charging, err := cc.Charging.BoolGetter(ctx)
 	if err != nil {
 		return nil, fmt.Errorf("charging: %w", err)
 	}
 
-	o := NewCombinedProvider(plugged, charging)
+	o := NewCombinedPlugin(plugged, charging)
 
 	return o, nil
 }
 
-// NewCombinedProvider creates provider for OpenWB status converted from MQTT topics
-func NewCombinedProvider(plugged, charging func() (bool, error)) *combinedProvider {
-	return &combinedProvider{
+// NewCombinedPlugin creates provider for OpenWB status converted from MQTT topics
+func NewCombinedPlugin(plugged, charging func() (bool, error)) *combinedPlugin {
+	return &combinedPlugin{
 		plugged:  plugged,
 		charging: charging,
 	}
 }
 
-var _ StringProvider = (*combinedProvider)(nil)
+var _ StringGetter = (*combinedPlugin)(nil)
 
 // StringGetter returns string from OpenWB charging/ plugged status
-func (o *combinedProvider) StringGetter() (func() (string, error), error) {
+func (o *combinedPlugin) StringGetter() (func() (string, error), error) {
 	return func() (string, error) {
 		charging, err := o.charging()
 		if err != nil {
diff --git a/plugin/config.go b/plugin/config.go
new file mode 100644
index 00000000..a743768a
--- /dev/null
+++ b/plugin/config.go
@@ -0,0 +1,160 @@
+package plugin
+
+import (
+	"context"
+	"fmt"
+
+	reg "github.com/evcc-io/evcc/util/registry"
+)
+
+var registry = reg.New[Plugin]("plugin")
+
+// plugin types
+type (
+	Plugin  interface{}
+	Getters interface {
+		StringGetter
+		FloatGetter
+		IntGetter
+		BoolGetter
+	}
+	StringGetter interface {
+		StringGetter() (func() (string, error), error)
+	}
+	FloatGetter interface {
+		FloatGetter() (func() (float64, error), error)
+	}
+	IntGetter interface {
+		IntGetter() (func() (int64, error), error)
+	}
+	BoolGetter interface {
+		BoolGetter() (func() (bool, error), error)
+	}
+	StringSetter interface {
+		StringSetter(param string) (func(string) error, error)
+	}
+	FloatSetter interface {
+		FloatSetter(param string) (func(float64) error, error)
+	}
+	IntSetter interface {
+		IntSetter(param string) (func(int64) error, error)
+	}
+	BoolSetter interface {
+		BoolSetter(param string) (func(bool) error, error)
+	}
+	BytesSetter interface {
+		BytesSetter(param string) (func([]byte) error, error)
+	}
+)
+
+// Config is the general plugin config
+type Config struct {
+	Source string
+	Other  map[string]any `mapstructure:",remain" yaml:",inline"`
+}
+
+func plugin[T any](typ string, ctx context.Context, config *Config) (T, error) {
+	var zero T
+
+	if config == nil {
+		return zero, nil
+	}
+
+	factory, err := registry.Get(config.Source)
+	if err != nil {
+		return zero, err
+	}
+
+	plugin, err := factory(ctx, config.Other)
+	if err != nil {
+		return zero, err
+	}
+
+	prov, ok := plugin.(T)
+	if !ok {
+		return zero, fmt.Errorf("invalid plugin source for type %s: %s", typ, config.Source)
+	}
+
+	return prov, nil
+}
+
+func (c *Config) IntGetter(ctx context.Context) (func() (int64, error), error) {
+	prov, err := plugin[IntGetter]("int", ctx, c)
+	if prov == nil || err != nil {
+		return nil, err
+	}
+
+	return prov.IntGetter()
+}
+
+func (c *Config) FloatGetter(ctx context.Context) (func() (float64, error), error) {
+	prov, err := plugin[FloatGetter]("float", ctx, c)
+	if prov == nil || err != nil {
+		return nil, err
+	}
+
+	return prov.FloatGetter()
+}
+
+func (c *Config) StringGetter(ctx context.Context) (func() (string, error), error) {
+	prov, err := plugin[StringGetter]("string", ctx, c)
+	if prov == nil || err != nil {
+		return nil, err
+	}
+
+	return prov.StringGetter()
+}
+
+func (c *Config) BoolGetter(ctx context.Context) (func() (bool, error), error) {
+	prov, err := plugin[BoolGetter]("bool", ctx, c)
+	if prov == nil || err != nil {
+		return nil, err
+	}
+
+	return prov.BoolGetter()
+}
+
+func (c *Config) IntSetter(ctx context.Context, param string) (func(int64) error, error) {
+	prov, err := plugin[IntSetter]("int", ctx, c)
+	if prov == nil || err != nil {
+		return nil, err
+	}
+
+	return prov.IntSetter(param)
+}
+
+func (c *Config) FloatSetter(ctx context.Context, param string) (func(float642 float64) error, error) {
+	prov, err := plugin[FloatSetter]("float", ctx, c)
+	if prov == nil || err != nil {
+		return nil, err
+	}
+
+	return prov.FloatSetter(param)
+}
+
+func (c *Config) StringSetter(ctx context.Context, param string) (func(string) error, error) {
+	prov, err := plugin[StringSetter]("string", ctx, c)
+	if prov == nil || err != nil {
+		return nil, err
+	}
+
+	return prov.StringSetter(param)
+}
+
+func (c *Config) BoolSetter(ctx context.Context, param string) (func(bool) error, error) {
+	prov, err := plugin[BoolSetter]("bool", ctx, c)
+	if prov == nil || err != nil {
+		return nil, err
+	}
+
+	return prov.BoolSetter(param)
+}
+
+func (c *Config) BytesSetter(ctx context.Context, param string) (func([]byte) error, error) {
+	prov, err := plugin[BytesSetter]("bytes", ctx, c)
+	if prov == nil || err != nil {
+		return nil, err
+	}
+
+	return prov.BytesSetter(param)
+}
diff --git a/provider/const.go b/plugin/const.go
similarity index 58%
rename from provider/const.go
rename to plugin/const.go
index d82b3b9c..c5ea8be5 100644
--- a/provider/const.go
+++ b/plugin/const.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -6,11 +6,11 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/evcc-io/evcc/provider/pipeline"
+	"github.com/evcc-io/evcc/plugin/pipeline"
 	"github.com/evcc-io/evcc/util"
 )
 
-type constProvider struct {
+type constPlugin struct {
 	ctx context.Context
 	str string
 	set Config
@@ -21,7 +21,7 @@ func init() {
 }
 
 // NewConstFromConfig creates const provider
-func NewConstFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewConstFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Value             string
 		pipeline.Settings `mapstructure:",squash"`
@@ -32,7 +32,7 @@ func NewConstFromConfig(ctx context.Context, other map[string]interface{}) (Prov
 		return nil, err
 	}
 
-	p := &constProvider{
+	p := &constPlugin{
 		ctx: ctx,
 		str: cc.Value,
 		set: cc.Set,
@@ -41,17 +41,17 @@ func NewConstFromConfig(ctx context.Context, other map[string]interface{}) (Prov
 	return p, nil
 }
 
-var _ StringProvider = (*constProvider)(nil)
+var _ StringGetter = (*constPlugin)(nil)
 
-func (p *constProvider) StringGetter() (func() (string, error), error) {
+func (p *constPlugin) StringGetter() (func() (string, error), error) {
 	return func() (string, error) {
 		return p.str, nil
 	}, nil
 }
 
-var _ IntProvider = (*constProvider)(nil)
+var _ IntGetter = (*constPlugin)(nil)
 
-func (p *constProvider) IntGetter() (func() (int64, error), error) {
+func (p *constPlugin) IntGetter() (func() (int64, error), error) {
 	val, err := strconv.ParseInt(p.str, 10, 64)
 	if err != nil && p.str == "" {
 		err = nil
@@ -62,9 +62,9 @@ func (p *constProvider) IntGetter() (func() (int64, error), error) {
 	}, err
 }
 
-var _ FloatProvider = (*constProvider)(nil)
+var _ FloatGetter = (*constPlugin)(nil)
 
-func (p *constProvider) FloatGetter() (func() (float64, error), error) {
+func (p *constPlugin) FloatGetter() (func() (float64, error), error) {
 	val, err := strconv.ParseFloat(p.str, 64)
 	if err != nil && p.str == "" {
 		err = nil
@@ -75,9 +75,9 @@ func (p *constProvider) FloatGetter() (func() (float64, error), error) {
 	}, err
 }
 
-var _ BoolProvider = (*constProvider)(nil)
+var _ BoolGetter = (*constPlugin)(nil)
 
-func (p *constProvider) BoolGetter() (func() (bool, error), error) {
+func (p *constPlugin) BoolGetter() (func() (bool, error), error) {
 	val, err := strconv.ParseBool(p.str)
 	if err != nil && p.str == "" {
 		err = nil
@@ -88,10 +88,10 @@ func (p *constProvider) BoolGetter() (func() (bool, error), error) {
 	}, err
 }
 
-var _ SetIntProvider = (*constProvider)(nil)
+var _ IntSetter = (*constPlugin)(nil)
 
-func (p *constProvider) IntSetter(param string) (func(int64) error, error) {
-	set, err := NewIntSetterFromConfig(p.ctx, param, p.set)
+func (p *constPlugin) IntSetter(param string) (func(int64) error, error) {
+	set, err := p.set.IntSetter(p.ctx, param)
 	if err != nil {
 		return nil, err
 	}
@@ -106,10 +106,10 @@ func (p *constProvider) IntSetter(param string) (func(int64) error, error) {
 	}, err
 }
 
-var _ SetFloatProvider = (*constProvider)(nil)
+var _ FloatSetter = (*constPlugin)(nil)
 
-func (p *constProvider) FloatSetter(param string) (func(float64) error, error) {
-	set, err := NewFloatSetterFromConfig(p.ctx, param, p.set)
+func (p *constPlugin) FloatSetter(param string) (func(float64) error, error) {
+	set, err := p.set.FloatSetter(p.ctx, param)
 	if err != nil {
 		return nil, err
 	}
@@ -124,10 +124,10 @@ func (p *constProvider) FloatSetter(param string) (func(float64) error, error) {
 	}, err
 }
 
-var _ SetBoolProvider = (*constProvider)(nil)
+var _ BoolSetter = (*constPlugin)(nil)
 
-func (p *constProvider) BoolSetter(param string) (func(bool) error, error) {
-	set, err := NewBoolSetterFromConfig(p.ctx, param, p.set)
+func (p *constPlugin) BoolSetter(param string) (func(bool) error, error) {
+	set, err := p.set.BoolSetter(p.ctx, param)
 	if err != nil {
 		return nil, err
 	}
@@ -142,10 +142,10 @@ func (p *constProvider) BoolSetter(param string) (func(bool) error, error) {
 	}, err
 }
 
-var _ SetBytesProvider = (*constProvider)(nil)
+var _ BytesSetter = (*constPlugin)(nil)
 
-func (p *constProvider) BytesSetter(param string) (func([]byte) error, error) {
-	set, err := NewBytesSetterFromConfig(p.ctx, param, p.set)
+func (p *constPlugin) BytesSetter(param string) (func([]byte) error, error) {
+	set, err := p.set.BytesSetter(p.ctx, param)
 	if err != nil {
 		return nil, err
 	}
diff --git a/provider/convert.go b/plugin/convert.go
similarity index 64%
rename from provider/convert.go
rename to plugin/convert.go
index 0eedb00d..9418a861 100644
--- a/provider/convert.go
+++ b/plugin/convert.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -8,7 +8,7 @@ import (
 	"github.com/evcc-io/evcc/util"
 )
 
-type convertProvider struct {
+type convertPlugin struct {
 	ctx     context.Context
 	Convert string
 	Set     Config
@@ -19,8 +19,8 @@ func init() {
 }
 
 // NewConvertFromConfig creates type conversion provider
-func NewConvertFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
-	cc := convertProvider{
+func NewConvertFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
+	cc := convertPlugin{
 		ctx: ctx,
 	}
 
@@ -31,12 +31,12 @@ func NewConvertFromConfig(ctx context.Context, other map[string]interface{}) (Pr
 	return &cc, nil
 }
 
-var _ SetFloatProvider = (*convertProvider)(nil)
+var _ FloatSetter = (*convertPlugin)(nil)
 
-func (o *convertProvider) FloatSetter(param string) (func(float64) error, error) {
+func (o *convertPlugin) FloatSetter(param string) (func(float64) error, error) {
 	switch o.Convert {
 	case "float2int":
-		set, err := NewIntSetterFromConfig(o.ctx, param, o.Set)
+		set, err := o.Set.IntSetter(o.ctx, param)
 
 		return func(val float64) error {
 			return set(int64(val))
@@ -47,19 +47,19 @@ func (o *convertProvider) FloatSetter(param string) (func(float64) error, error)
 	}
 }
 
-var _ SetIntProvider = (*convertProvider)(nil)
+var _ IntSetter = (*convertPlugin)(nil)
 
-func (o *convertProvider) IntSetter(param string) (func(int64) error, error) {
+func (o *convertPlugin) IntSetter(param string) (func(int64) error, error) {
 	switch o.Convert {
 	case "int2float":
-		set, err := NewFloatSetterFromConfig(o.ctx, param, o.Set)
+		set, err := o.Set.FloatSetter(o.ctx, param)
 
 		return func(val int64) error {
 			return set(float64(val))
 		}, err
 
 	case "int2bytes":
-		set, err := NewBytesSetterFromConfig(o.ctx, param, o.Set)
+		set, err := o.Set.BytesSetter(o.ctx, param)
 
 		return func(val int64) error {
 			b := make([]byte, 8)
diff --git a/provider/error.go b/plugin/error.go
similarity index 64%
rename from provider/error.go
rename to plugin/error.go
index 96087ccb..15babc2c 100644
--- a/provider/error.go
+++ b/plugin/error.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"fmt"
@@ -6,7 +6,7 @@ import (
 	"github.com/evcc-io/evcc/util"
 )
 
-type errorProvider struct {
+type errorPlugin struct {
 	err error
 }
 
@@ -15,7 +15,7 @@ func init() {
 }
 
 // NewErrorFromConfig creates error provider
-func NewErrorFromConfig(other map[string]interface{}) (Provider, error) {
+func NewErrorFromConfig(other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Error string
 	}
@@ -29,16 +29,16 @@ func NewErrorFromConfig(other map[string]interface{}) (Provider, error) {
 		return nil, fmt.Errorf("unknown error: %s", cc.Error)
 	}
 
-	o := &errorProvider{
+	o := &errorPlugin{
 		err: err,
 	}
 
 	return o, nil
 }
 
-var _ SetIntProvider = (*errorProvider)(nil)
+var _ IntSetter = (*errorPlugin)(nil)
 
-func (o *errorProvider) IntSetter(param string) (func(int64) error, error) {
+func (o *errorPlugin) IntSetter(param string) (func(int64) error, error) {
 	return func(int64) error {
 		return o.err
 	}, nil
diff --git a/provider/getter.go b/plugin/getter.go
similarity index 77%
rename from provider/getter.go
rename to plugin/getter.go
index 17b1394f..b693608f 100644
--- a/provider/getter.go
+++ b/plugin/getter.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"strconv"
@@ -7,22 +7,22 @@ import (
 )
 
 type getter struct {
-	sp    StringProvider
+	get   StringGetter
 	scale float64
 }
 
-func defaultGetters(sp StringProvider, scale float64) *getter {
+func defaultGetters(get StringGetter, scale float64) *getter {
 	return &getter{
-		sp:    sp,
+		get:   get,
 		scale: scale,
 	}
 }
 
-var _ FloatProvider = (*getter)(nil)
+var _ FloatGetter = (*getter)(nil)
 
 // FloatGetter parses float from exec result
 func (p *getter) FloatGetter() (func() (float64, error), error) {
-	g, err := p.sp.StringGetter()
+	g, err := p.get.StringGetter()
 
 	return func() (float64, error) {
 		s, err := g()
@@ -39,7 +39,7 @@ func (p *getter) FloatGetter() (func() (float64, error), error) {
 	}, err
 }
 
-var _ IntProvider = (*getter)(nil)
+var _ IntGetter = (*getter)(nil)
 
 // IntGetter parses int64 from exec result
 func (p *getter) IntGetter() (func() (int64, error), error) {
@@ -55,11 +55,11 @@ func (p *getter) IntGetter() (func() (int64, error), error) {
 	}, err
 }
 
-var _ BoolProvider = (*getter)(nil)
+var _ BoolGetter = (*getter)(nil)
 
 // BoolGetter parses bool from exec result. "on", "true" and 1 are considered truish.
 func (p *getter) BoolGetter() (func() (bool, error), error) {
-	g, err := p.sp.StringGetter()
+	g, err := p.get.StringGetter()
 
 	return func() (bool, error) {
 		s, err := g()
diff --git a/provider/go.go b/plugin/go.go
similarity index 88%
rename from provider/go.go
rename to plugin/go.go
index ce585c9e..f8aab0d2 100644
--- a/provider/go.go
+++ b/plugin/go.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -7,7 +7,7 @@ import (
 	"reflect"
 
 	"github.com/cenkalti/backoff/v4"
-	"github.com/evcc-io/evcc/provider/golang"
+	"github.com/evcc-io/evcc/plugin/golang"
 	"github.com/evcc-io/evcc/util"
 	"github.com/traefik/yaegi/interp"
 )
@@ -21,11 +21,11 @@ type Go struct {
 }
 
 func init() {
-	registry.AddCtx("go", NewGoProviderFromConfig)
+	registry.AddCtx("go", NewGoPluginFromConfig)
 }
 
-// NewGoProviderFromConfig creates a Go provider
-func NewGoProviderFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+// NewGoPluginFromConfig creates a Go provider
+func NewGoPluginFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		VM     string
 		Script string
@@ -62,7 +62,7 @@ func NewGoProviderFromConfig(ctx context.Context, other map[string]interface{})
 	return p, nil
 }
 
-var _ FloatProvider = (*Go)(nil)
+var _ FloatGetter = (*Go)(nil)
 
 // FloatGetter parses float from request
 func (p *Go) FloatGetter() (func() (float64, error), error) {
@@ -81,7 +81,7 @@ func (p *Go) FloatGetter() (func() (float64, error), error) {
 	}, nil
 }
 
-var _ IntProvider = (*Go)(nil)
+var _ IntGetter = (*Go)(nil)
 
 // IntGetter parses int64 from request
 func (p *Go) IntGetter() (func() (int64, error), error) {
@@ -100,7 +100,7 @@ func (p *Go) IntGetter() (func() (int64, error), error) {
 	}, nil
 }
 
-var _ StringProvider = (*Go)(nil)
+var _ StringGetter = (*Go)(nil)
 
 // StringGetter parses string from request
 func (p *Go) StringGetter() (func() (string, error), error) {
@@ -119,7 +119,7 @@ func (p *Go) StringGetter() (func() (string, error), error) {
 	}, nil
 }
 
-var _ BoolProvider = (*Go)(nil)
+var _ BoolGetter = (*Go)(nil)
 
 // BoolGetter parses bool from request
 func (p *Go) BoolGetter() (func() (bool, error), error) {
@@ -192,7 +192,7 @@ func (p *Go) setParam(param string, val any) error {
 	return err
 }
 
-var _ SetIntProvider = (*Go)(nil)
+var _ IntSetter = (*Go)(nil)
 
 // IntSetter sends int request
 func (p *Go) IntSetter(param string) (func(int64) error, error) {
@@ -201,7 +201,7 @@ func (p *Go) IntSetter(param string) (func(int64) error, error) {
 	}, nil
 }
 
-var _ SetFloatProvider = (*Go)(nil)
+var _ FloatSetter = (*Go)(nil)
 
 // FloatSetter sends float request
 func (p *Go) FloatSetter(param string) (func(float64) error, error) {
@@ -210,7 +210,7 @@ func (p *Go) FloatSetter(param string) (func(float64) error, error) {
 	}, nil
 }
 
-var _ SetStringProvider = (*Go)(nil)
+var _ StringSetter = (*Go)(nil)
 
 // StringSetter sends string request
 func (p *Go) StringSetter(param string) (func(string) error, error) {
@@ -219,7 +219,7 @@ func (p *Go) StringSetter(param string) (func(string) error, error) {
 	}, nil
 }
 
-var _ SetBoolProvider = (*Go)(nil)
+var _ BoolSetter = (*Go)(nil)
 
 // BoolSetter sends bool request
 func (p *Go) BoolSetter(param string) (func(bool) error, error) {
diff --git a/provider/golang/registry.go b/plugin/golang/registry.go
similarity index 93%
rename from provider/golang/registry.go
rename to plugin/golang/registry.go
index 72f0bcf2..a937bcad 100644
--- a/provider/golang/registry.go
+++ b/plugin/golang/registry.go
@@ -4,7 +4,7 @@ import (
 	"strings"
 	"sync"
 
-	"github.com/evcc-io/evcc/provider/golang/stdlib"
+	"github.com/evcc-io/evcc/plugin/golang/stdlib"
 	"github.com/traefik/yaegi/interp"
 )
 
diff --git a/provider/golang/stdlib/fmt.go b/plugin/golang/stdlib/fmt.go
similarity index 100%
rename from provider/golang/stdlib/fmt.go
rename to plugin/golang/stdlib/fmt.go
diff --git a/provider/golang/stdlib/generate.go b/plugin/golang/stdlib/generate.go
similarity index 100%
rename from provider/golang/stdlib/generate.go
rename to plugin/golang/stdlib/generate.go
diff --git a/provider/golang/stdlib/math.go b/plugin/golang/stdlib/math.go
similarity index 100%
rename from provider/golang/stdlib/math.go
rename to plugin/golang/stdlib/math.go
diff --git a/provider/golang/stdlib/strings.go b/plugin/golang/stdlib/strings.go
similarity index 100%
rename from provider/golang/stdlib/strings.go
rename to plugin/golang/stdlib/strings.go
diff --git a/provider/golang/stdlib/time.go b/plugin/golang/stdlib/time.go
similarity index 100%
rename from provider/golang/stdlib/time.go
rename to plugin/golang/stdlib/time.go
diff --git a/provider/helper.go b/plugin/helper.go
similarity index 98%
rename from provider/helper.go
rename to plugin/helper.go
index 41df2d41..7f752025 100644
--- a/provider/helper.go
+++ b/plugin/helper.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
diff --git a/provider/http.go b/plugin/http.go
similarity index 92%
rename from provider/http.go
rename to plugin/http.go
index 6bbe7ad5..cb161c7b 100644
--- a/provider/http.go
+++ b/plugin/http.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -8,7 +8,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/evcc-io/evcc/provider/pipeline"
+	"github.com/evcc-io/evcc/plugin/pipeline"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/transport"
@@ -31,7 +31,7 @@ type HTTP struct {
 }
 
 func init() {
-	registry.AddCtx("http", NewHTTPProviderFromConfig)
+	registry.AddCtx("http", NewHTTPPluginFromConfig)
 }
 
 var mc = httpcache.NewMemoryCache()
@@ -41,8 +41,8 @@ type Auth struct {
 	Type, User, Password string
 }
 
-// NewHTTPProviderFromConfig creates a HTTP provider
-func NewHTTPProviderFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+// NewHTTPPluginFromConfig creates a HTTP provider
+func NewHTTPPluginFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	cc := struct {
 		URI, Method       string
 		Headers           map[string]string
@@ -217,7 +217,7 @@ func (p *HTTP) set(param string, val interface{}) error {
 	return err
 }
 
-var _ SetIntProvider = (*HTTP)(nil)
+var _ IntSetter = (*HTTP)(nil)
 
 // IntSetter sends int request
 func (p *HTTP) IntSetter(param string) (func(int64) error, error) {
@@ -226,7 +226,7 @@ func (p *HTTP) IntSetter(param string) (func(int64) error, error) {
 	}, nil
 }
 
-var _ SetFloatProvider = (*HTTP)(nil)
+var _ FloatSetter = (*HTTP)(nil)
 
 // FloatSetter sends int request
 func (p *HTTP) FloatSetter(param string) (func(float64) error, error) {
@@ -235,7 +235,7 @@ func (p *HTTP) FloatSetter(param string) (func(float64) error, error) {
 	}, nil
 }
 
-var _ SetStringProvider = (*HTTP)(nil)
+var _ StringSetter = (*HTTP)(nil)
 
 // StringSetter sends string request
 func (p *HTTP) StringSetter(param string) (func(string) error, error) {
@@ -244,7 +244,7 @@ func (p *HTTP) StringSetter(param string) (func(string) error, error) {
 	}, nil
 }
 
-var _ SetBoolProvider = (*HTTP)(nil)
+var _ BoolSetter = (*HTTP)(nil)
 
 // BoolSetter sends bool request
 func (p *HTTP) BoolSetter(param string) (func(bool) error, error) {
diff --git a/provider/ignore.go b/plugin/ignore.go
similarity index 54%
rename from provider/ignore.go
rename to plugin/ignore.go
index 60b53c04..1ec72b84 100644
--- a/provider/ignore.go
+++ b/plugin/ignore.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -7,7 +7,7 @@ import (
 	"github.com/evcc-io/evcc/util"
 )
 
-type ignoreProvider struct {
+type ignorePlugin struct {
 	ctx context.Context
 	err string
 	set Config
@@ -18,7 +18,7 @@ func init() {
 }
 
 // NewIgnoreFromConfig creates const provider
-func NewIgnoreFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewIgnoreFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Error string
 		Set   Config
@@ -28,7 +28,7 @@ func NewIgnoreFromConfig(ctx context.Context, other map[string]interface{}) (Pro
 		return nil, err
 	}
 
-	o := &ignoreProvider{
+	o := &ignorePlugin{
 		ctx: ctx,
 		err: cc.Error,
 		set: cc.Set,
@@ -37,7 +37,7 @@ func NewIgnoreFromConfig(ctx context.Context, other map[string]interface{}) (Pro
 	return o, nil
 }
 
-var _ SetIntProvider = (*ignoreProvider)(nil)
+var _ IntSetter = (*ignorePlugin)(nil)
 
 func ignoreError[T any](fun func(T) error, match string) func(T) error {
 	return func(val T) error {
@@ -48,8 +48,8 @@ func ignoreError[T any](fun func(T) error, match string) func(T) error {
 	}
 }
 
-func (o *ignoreProvider) IntSetter(param string) (func(int64) error, error) {
-	set, err := NewIntSetterFromConfig(o.ctx, param, o.set)
+func (o *ignorePlugin) IntSetter(param string) (func(int64) error, error) {
+	set, err := o.set.IntSetter(o.ctx, param)
 	if err != nil {
 		return nil, err
 	}
@@ -57,10 +57,10 @@ func (o *ignoreProvider) IntSetter(param string) (func(int64) error, error) {
 	return ignoreError(set, o.err), nil
 }
 
-var _ SetFloatProvider = (*ignoreProvider)(nil)
+var _ FloatSetter = (*ignorePlugin)(nil)
 
-func (o *ignoreProvider) FloatSetter(param string) (func(float64) error, error) {
-	set, err := NewFloatSetterFromConfig(o.ctx, param, o.set)
+func (o *ignorePlugin) FloatSetter(param string) (func(float64) error, error) {
+	set, err := o.set.FloatSetter(o.ctx, param)
 	if err != nil {
 		return nil, err
 	}
@@ -68,10 +68,10 @@ func (o *ignoreProvider) FloatSetter(param string) (func(float64) error, error)
 	return ignoreError(set, o.err), nil
 }
 
-var _ SetBoolProvider = (*ignoreProvider)(nil)
+var _ BoolSetter = (*ignorePlugin)(nil)
 
-func (o *ignoreProvider) BoolSetter(param string) (func(bool) error, error) {
-	set, err := NewBoolSetterFromConfig(o.ctx, param, o.set)
+func (o *ignorePlugin) BoolSetter(param string) (func(bool) error, error) {
+	set, err := o.set.BoolSetter(o.ctx, param)
 	if err != nil {
 		return nil, err
 	}
@@ -79,10 +79,10 @@ func (o *ignoreProvider) BoolSetter(param string) (func(bool) error, error) {
 	return ignoreError(set, o.err), nil
 }
 
-var _ SetBytesProvider = (*ignoreProvider)(nil)
+var _ BytesSetter = (*ignorePlugin)(nil)
 
-func (o *ignoreProvider) BytesSetter(param string) (func([]byte) error, error) {
-	set, err := NewBytesSetterFromConfig(o.ctx, param, o.set)
+func (o *ignorePlugin) BytesSetter(param string) (func([]byte) error, error) {
+	set, err := o.set.BytesSetter(o.ctx, param)
 	if err != nil {
 		return nil, err
 	}
diff --git a/provider/javascript.go b/plugin/javascript.go
similarity index 86%
rename from provider/javascript.go
rename to plugin/javascript.go
index 27dc443e..89d38184 100644
--- a/provider/javascript.go
+++ b/plugin/javascript.go
@@ -1,11 +1,11 @@
-package provider
+package plugin
 
 import (
 	"context"
 	"fmt"
 
 	"github.com/cenkalti/backoff/v4"
-	"github.com/evcc-io/evcc/provider/javascript"
+	"github.com/evcc-io/evcc/plugin/javascript"
 	"github.com/evcc-io/evcc/util"
 	"github.com/robertkrimen/otto"
 	"github.com/spf13/cast"
@@ -20,11 +20,11 @@ type Javascript struct {
 }
 
 func init() {
-	registry.AddCtx("js", NewJavascriptProviderFromConfig)
+	registry.AddCtx("js", NewJavascriptPluginFromConfig)
 }
 
-// NewJavascriptProviderFromConfig creates a Javascript provider
-func NewJavascriptProviderFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+// NewJavascriptPluginFromConfig creates a Javascript provider
+func NewJavascriptPluginFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		VM     string
 		Script string
@@ -61,7 +61,7 @@ func NewJavascriptProviderFromConfig(ctx context.Context, other map[string]inter
 	return p, nil
 }
 
-var _ FloatProvider = (*Javascript)(nil)
+var _ FloatGetter = (*Javascript)(nil)
 
 // FloatGetter parses float from request
 func (p *Javascript) FloatGetter() (func() (float64, error), error) {
@@ -75,7 +75,7 @@ func (p *Javascript) FloatGetter() (func() (float64, error), error) {
 	}, nil
 }
 
-var _ IntProvider = (*Javascript)(nil)
+var _ IntGetter = (*Javascript)(nil)
 
 // IntGetter parses int64 from request
 func (p *Javascript) IntGetter() (func() (int64, error), error) {
@@ -89,7 +89,7 @@ func (p *Javascript) IntGetter() (func() (int64, error), error) {
 	}, nil
 }
 
-var _ StringProvider = (*Javascript)(nil)
+var _ StringGetter = (*Javascript)(nil)
 
 // StringGetter parses string from request
 func (p *Javascript) StringGetter() (func() (string, error), error) {
@@ -103,7 +103,7 @@ func (p *Javascript) StringGetter() (func() (string, error), error) {
 	}, nil
 }
 
-var _ BoolProvider = (*Javascript)(nil)
+var _ BoolGetter = (*Javascript)(nil)
 
 // BoolGetter parses bool from request
 func (p *Javascript) BoolGetter() (func() (bool, error), error) {
@@ -185,7 +185,7 @@ func (p *Javascript) setParamSync(param string, val any) error {
 	return p.setParam(param, val)
 }
 
-var _ SetIntProvider = (*Javascript)(nil)
+var _ IntSetter = (*Javascript)(nil)
 
 // IntSetter sends int request
 func (p *Javascript) IntSetter(param string) (func(int64) error, error) {
@@ -194,7 +194,7 @@ func (p *Javascript) IntSetter(param string) (func(int64) error, error) {
 	}, nil
 }
 
-var _ SetFloatProvider = (*Javascript)(nil)
+var _ FloatSetter = (*Javascript)(nil)
 
 // FloatSetter sends float request
 func (p *Javascript) FloatSetter(param string) (func(float64) error, error) {
@@ -203,7 +203,7 @@ func (p *Javascript) FloatSetter(param string) (func(float64) error, error) {
 	}, nil
 }
 
-var _ SetStringProvider = (*Javascript)(nil)
+var _ StringSetter = (*Javascript)(nil)
 
 // StringSetter sends string request
 func (p *Javascript) StringSetter(param string) (func(string) error, error) {
@@ -212,7 +212,7 @@ func (p *Javascript) StringSetter(param string) (func(string) error, error) {
 	}, nil
 }
 
-var _ SetBoolProvider = (*Javascript)(nil)
+var _ BoolSetter = (*Javascript)(nil)
 
 // BoolSetter sends bool request
 func (p *Javascript) BoolSetter(param string) (func(bool) error, error) {
diff --git a/provider/javascript/registry.go b/plugin/javascript/registry.go
similarity index 100%
rename from provider/javascript/registry.go
rename to plugin/javascript/registry.go
diff --git a/provider/map.go b/plugin/map.go
similarity index 70%
rename from provider/map.go
rename to plugin/map.go
index 894bbcac..de6c240c 100644
--- a/provider/map.go
+++ b/plugin/map.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -7,7 +7,7 @@ import (
 	"github.com/evcc-io/evcc/util"
 )
 
-type mapProvider struct {
+type mapPlugin struct {
 	ctx      context.Context
 	values   map[int64]int64
 	get, set Config
@@ -18,7 +18,7 @@ func init() {
 }
 
 // NewMapFromConfig creates type conversion provider
-func NewMapFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewMapFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Values   map[int64]int64
 		Get, Set Config
@@ -32,7 +32,7 @@ func NewMapFromConfig(ctx context.Context, other map[string]interface{}) (Provid
 		return nil, fmt.Errorf("missing values")
 	}
 
-	o := &mapProvider{
+	o := &mapPlugin{
 		ctx:    ctx,
 		get:    cc.Get,
 		set:    cc.Set,
@@ -42,10 +42,10 @@ func NewMapFromConfig(ctx context.Context, other map[string]interface{}) (Provid
 	return o, nil
 }
 
-var _ IntProvider = (*mapProvider)(nil)
+var _ IntGetter = (*mapPlugin)(nil)
 
-func (o *mapProvider) IntGetter() (func() (int64, error), error) {
-	get, err := NewIntGetterFromConfig(o.ctx, o.get)
+func (o *mapPlugin) IntGetter() (func() (int64, error), error) {
+	get, err := o.get.IntGetter(o.ctx)
 
 	return func() (int64, error) {
 		val, err := get()
@@ -62,10 +62,10 @@ func (o *mapProvider) IntGetter() (func() (int64, error), error) {
 	}, err
 }
 
-var _ SetIntProvider = (*mapProvider)(nil)
+var _ IntSetter = (*mapPlugin)(nil)
 
-func (o *mapProvider) IntSetter(param string) (func(int64) error, error) {
-	set, err := NewIntSetterFromConfig(o.ctx, param, o.set)
+func (o *mapPlugin) IntSetter(param string) (func(int64) error, error) {
+	set, err := o.set.IntSetter(o.ctx, param)
 
 	return func(val int64) error {
 		m, ok := o.values[val]
diff --git a/provider/modbus.go b/plugin/modbus.go
similarity index 90%
rename from provider/modbus.go
rename to plugin/modbus.go
index 8fbd936a..f1f016af 100644
--- a/provider/modbus.go
+++ b/plugin/modbus.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"bytes"
@@ -26,7 +26,7 @@ func init() {
 }
 
 // NewModbusFromConfig creates Modbus plugin
-func NewModbusFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewModbusFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	cc := struct {
 		modbus.Settings `mapstructure:",squash"`
 		Register        modbus.Register
@@ -91,7 +91,7 @@ func (m *Modbus) readBytes(op modbus.RegisterOperation) ([]byte, error) {
 	}
 }
 
-var _ FloatProvider = (*Modbus)(nil)
+var _ FloatGetter = (*Modbus)(nil)
 
 // FloatGetter implements func() (float64, error)
 func (m *Modbus) FloatGetter() (func() (f float64, err error), error) {
@@ -115,7 +115,7 @@ func (m *Modbus) FloatGetter() (func() (f float64, err error), error) {
 	}, nil
 }
 
-var _ IntProvider = (*Modbus)(nil)
+var _ IntGetter = (*Modbus)(nil)
 
 // IntGetter implements IntProvider
 func (m *Modbus) IntGetter() (func() (int64, error), error) {
@@ -127,7 +127,7 @@ func (m *Modbus) IntGetter() (func() (int64, error), error) {
 	}, err
 }
 
-var _ StringProvider = (*Modbus)(nil)
+var _ StringGetter = (*Modbus)(nil)
 
 // StringGetter implements StringProvider
 func (m *Modbus) StringGetter() (func() (string, error), error) {
@@ -146,7 +146,7 @@ func (m *Modbus) StringGetter() (func() (string, error), error) {
 	}, nil
 }
 
-var _ BoolProvider = (*Modbus)(nil)
+var _ BoolGetter = (*Modbus)(nil)
 
 // BoolGetter implements BoolProvider
 func (m *Modbus) BoolGetter() (func() (bool, error), error) {
@@ -198,16 +198,16 @@ func (m *Modbus) writeFunc() (func(float64) error, error) {
 	}, nil
 }
 
-var _ SetFloatProvider = (*Modbus)(nil)
+var _ FloatSetter = (*Modbus)(nil)
 
-// FloatSetter implements SetFloatProvider
+// FloatSetter implements FloatSetter
 func (m *Modbus) FloatSetter(_ string) (func(float64) error, error) {
 	return m.writeFunc()
 }
 
-var _ SetIntProvider = (*Modbus)(nil)
+var _ IntSetter = (*Modbus)(nil)
 
-// IntSetter implements SetIntProvider
+// IntSetter implements IntSetter
 func (m *Modbus) IntSetter(_ string) (func(int64) error, error) {
 	fun, err := m.writeFunc()
 	if err != nil {
@@ -219,9 +219,9 @@ func (m *Modbus) IntSetter(_ string) (func(int64) error, error) {
 	}, nil
 }
 
-var _ SetBoolProvider = (*Modbus)(nil)
+var _ BoolSetter = (*Modbus)(nil)
 
-// BoolSetter implements SetBoolProvider
+// BoolSetter implements BoolSetter
 func (m *Modbus) BoolSetter(param string) (func(bool) error, error) {
 	set, err := m.IntSetter(param)
 
@@ -235,9 +235,9 @@ func (m *Modbus) BoolSetter(param string) (func(bool) error, error) {
 	}, err
 }
 
-var _ SetBytesProvider = (*Modbus)(nil)
+var _ BytesSetter = (*Modbus)(nil)
 
-// BytesSetter implements SetBytesProvider
+// BytesSetter implements BytesSetter
 func (m *Modbus) BytesSetter(_ string) (func([]byte) error, error) {
 	op, err := m.reg.Operation()
 	if err != nil {
diff --git a/provider/mqtt.go b/plugin/mqtt.go
similarity index 89%
rename from provider/mqtt.go
rename to plugin/mqtt.go
index a1b9d081..d733417b 100644
--- a/provider/mqtt.go
+++ b/plugin/mqtt.go
@@ -1,11 +1,11 @@
-package provider
+package plugin
 
 import (
 	"context"
 	"time"
 
-	"github.com/evcc-io/evcc/provider/mqtt"
-	"github.com/evcc-io/evcc/provider/pipeline"
+	"github.com/evcc-io/evcc/plugin/mqtt"
+	"github.com/evcc-io/evcc/plugin/pipeline"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -22,11 +22,11 @@ type Mqtt struct {
 }
 
 func init() {
-	registry.AddCtx("mqtt", NewMqttFromConfig)
+	registry.AddCtx("mqtt", NewMqttPluginFromConfig)
 }
 
-// NewMqttFromConfig creates Mqtt provider
-func NewMqttFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+// NewMqttPluginFromConfig creates Mqtt provider
+func NewMqttPluginFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	cc := struct {
 		mqtt.Config       `mapstructure:",squash"`
 		Topic, Payload    string // Payload only applies to setters
@@ -120,7 +120,7 @@ func (m *Mqtt) StringGetter() (func() (string, error), error) {
 	return h.value, err
 }
 
-var _ SetIntProvider = (*Mqtt)(nil)
+var _ IntSetter = (*Mqtt)(nil)
 
 // IntSetter publishes topic with parameter replaced by int value
 func (m *Mqtt) IntSetter(param string) (func(int64) error, error) {
@@ -134,7 +134,7 @@ func (m *Mqtt) IntSetter(param string) (func(int64) error, error) {
 	}, nil
 }
 
-var _ SetFloatProvider = (*Mqtt)(nil)
+var _ FloatSetter = (*Mqtt)(nil)
 
 // FloatSetter publishes topic with parameter replaced by float value
 func (m *Mqtt) FloatSetter(param string) (func(float64) error, error) {
@@ -148,7 +148,7 @@ func (m *Mqtt) FloatSetter(param string) (func(float64) error, error) {
 	}, nil
 }
 
-var _ SetBoolProvider = (*Mqtt)(nil)
+var _ BoolSetter = (*Mqtt)(nil)
 
 // BoolSetter invokes script with parameter replaced by bool value
 func (m *Mqtt) BoolSetter(param string) (func(bool) error, error) {
@@ -162,7 +162,7 @@ func (m *Mqtt) BoolSetter(param string) (func(bool) error, error) {
 	}, nil
 }
 
-var _ SetStringProvider = (*Mqtt)(nil)
+var _ StringSetter = (*Mqtt)(nil)
 
 // StringSetter invokes script with parameter replaced by string value
 func (m *Mqtt) StringSetter(param string) (func(string) error, error) {
diff --git a/provider/mqtt/client.go b/plugin/mqtt/client.go
similarity index 100%
rename from provider/mqtt/client.go
rename to plugin/mqtt/client.go
diff --git a/provider/mqtt/registry.go b/plugin/mqtt/registry.go
similarity index 100%
rename from provider/mqtt/registry.go
rename to plugin/mqtt/registry.go
diff --git a/provider/mqtt_handler.go b/plugin/mqtt_handler.go
similarity index 92%
rename from provider/mqtt_handler.go
rename to plugin/mqtt_handler.go
index e65f42da..f993a5ce 100644
--- a/provider/mqtt_handler.go
+++ b/plugin/mqtt_handler.go
@@ -1,7 +1,7 @@
-package provider
+package plugin
 
 import (
-	"github.com/evcc-io/evcc/provider/pipeline"
+	"github.com/evcc-io/evcc/plugin/pipeline"
 	"github.com/evcc-io/evcc/util"
 )
 
diff --git a/provider/mqtt_timeout.go b/plugin/mqtt_timeout.go
similarity index 75%
rename from provider/mqtt_timeout.go
rename to plugin/mqtt_timeout.go
index 6ae0044d..ba2fd845 100644
--- a/provider/mqtt_timeout.go
+++ b/plugin/mqtt_timeout.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import "encoding/json"
 
@@ -11,7 +11,7 @@ func NewTimeoutHandler(ticker func() (string, error)) *TimeoutHandler {
 	return &TimeoutHandler{ticker}
 }
 
-func (h *TimeoutHandler) BoolGetter(p BoolProvider) (func() (bool, error), error) {
+func (h *TimeoutHandler) BoolGetter(p BoolGetter) (func() (bool, error), error) {
 	g, err := p.BoolGetter()
 	if err != nil {
 		return nil, err
@@ -25,7 +25,7 @@ func (h *TimeoutHandler) BoolGetter(p BoolProvider) (func() (bool, error), error
 	}, nil
 }
 
-func (h *TimeoutHandler) FloatGetter(p FloatProvider) (func() (float64, error), error) {
+func (h *TimeoutHandler) FloatGetter(p FloatGetter) (func() (float64, error), error) {
 	g, err := p.FloatGetter()
 	if err != nil {
 		return nil, err
@@ -39,7 +39,7 @@ func (h *TimeoutHandler) FloatGetter(p FloatProvider) (func() (float64, error),
 	}, nil
 }
 
-func (h *TimeoutHandler) StringGetter(p StringProvider) (func() (string, error), error) {
+func (h *TimeoutHandler) StringGetter(p StringGetter) (func() (string, error), error) {
 	g, err := p.StringGetter()
 	if err != nil {
 		return nil, err
@@ -53,7 +53,7 @@ func (h *TimeoutHandler) StringGetter(p StringProvider) (func() (string, error),
 	}, nil
 }
 
-func (h *TimeoutHandler) JsonGetter(p StringProvider) (func(any) error, error) {
+func (h *TimeoutHandler) JsonGetter(p StringGetter) (func(any) error, error) {
 	g, err := p.StringGetter()
 	if err != nil {
 		return nil, err
diff --git a/provider/pipeline/pipeline.go b/plugin/pipeline/pipeline.go
similarity index 100%
rename from provider/pipeline/pipeline.go
rename to plugin/pipeline/pipeline.go
diff --git a/provider/pipeline/pipeline_test.go b/plugin/pipeline/pipeline_test.go
similarity index 100%
rename from provider/pipeline/pipeline_test.go
rename to plugin/pipeline/pipeline_test.go
diff --git a/provider/prometheus.go b/plugin/prometheus.go
similarity index 93%
rename from provider/prometheus.go
rename to plugin/prometheus.go
index ef244601..45f0ff77 100644
--- a/provider/prometheus.go
+++ b/plugin/prometheus.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -26,7 +26,7 @@ func init() {
 	registry.Add("prometheus", NewPrometheusFromConfig)
 }
 
-func NewPrometheusFromConfig(other map[string]interface{}) (Provider, error) {
+func NewPrometheusFromConfig(other map[string]interface{}) (Plugin, error) {
 	cc := struct {
 		Uri, Query string
 		Timeout    time.Duration
@@ -83,7 +83,7 @@ func (p *Prometheus) Query() (model.Value, error) {
 	return res, nil
 }
 
-var _ FloatProvider = (*Prometheus)(nil)
+var _ FloatGetter = (*Prometheus)(nil)
 
 // FloatGetter expects scalar value from query response as float
 func (p *Prometheus) FloatGetter() (func() (float64, error), error) {
@@ -102,7 +102,7 @@ func (p *Prometheus) FloatGetter() (func() (float64, error), error) {
 	}, nil
 }
 
-var _ IntProvider = (*Prometheus)(nil)
+var _ IntGetter = (*Prometheus)(nil)
 
 // IntGetter expects scalar value from query response as int
 func (p *Prometheus) IntGetter() (func() (int64, error), error) {
diff --git a/provider/random.go b/plugin/random.go
similarity index 67%
rename from provider/random.go
rename to plugin/random.go
index 769823fa..5b6040a7 100644
--- a/provider/random.go
+++ b/plugin/random.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -8,7 +8,7 @@ import (
 	"github.com/evcc-io/evcc/util"
 )
 
-type randomProvider struct {
+type randomPlugin struct {
 	ctx context.Context
 	set Config
 }
@@ -18,7 +18,7 @@ func init() {
 }
 
 // NewRandomFromConfig creates random provider
-func NewRandomFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewRandomFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Set Config
 	}
@@ -27,7 +27,7 @@ func NewRandomFromConfig(ctx context.Context, other map[string]interface{}) (Pro
 		return nil, err
 	}
 
-	o := &randomProvider{
+	o := &randomPlugin{
 		ctx: ctx,
 		set: cc.Set,
 	}
@@ -35,10 +35,10 @@ func NewRandomFromConfig(ctx context.Context, other map[string]interface{}) (Pro
 	return o, nil
 }
 
-var _ SetIntProvider = (*randomProvider)(nil)
+var _ IntSetter = (*randomPlugin)(nil)
 
-func (o *randomProvider) IntSetter(param string) (func(int64) error, error) {
-	set, err := NewIntSetterFromConfig(o.ctx, param, o.set)
+func (o *randomPlugin) IntSetter(param string) (func(int64) error, error) {
+	set, err := o.set.IntSetter(o.ctx, param)
 	if err != nil {
 		return nil, err
 	}
diff --git a/provider/script.go b/plugin/script.go
similarity index 83%
rename from provider/script.go
rename to plugin/script.go
index 0a9f8355..94ab81e9 100644
--- a/provider/script.go
+++ b/plugin/script.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -7,7 +7,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/evcc-io/evcc/provider/pipeline"
+	"github.com/evcc-io/evcc/plugin/pipeline"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/kballard/go-shellquote"
@@ -27,11 +27,11 @@ type Script struct {
 }
 
 func init() {
-	registry.Add("script", NewScriptProviderFromConfig)
+	registry.Add("script", NewScriptPluginFromConfig)
 }
 
-// NewScriptProviderFromConfig creates a script provider.
-func NewScriptProviderFromConfig(other map[string]interface{}) (Provider, error) {
+// NewScriptPluginFromConfig creates a script plugin.
+func NewScriptPluginFromConfig(other map[string]interface{}) (Plugin, error) {
 	cc := struct {
 		Cmd               string
 		pipeline.Settings `mapstructure:",squash"`
@@ -47,7 +47,7 @@ func NewScriptProviderFromConfig(other map[string]interface{}) (Provider, error)
 		return nil, err
 	}
 
-	p, err := NewScriptProvider(cc.Cmd, cc.Timeout, cc.Scale, cc.Cache)
+	p, err := NewScripPlugin(cc.Cmd, cc.Timeout, cc.Scale, cc.Cache)
 	p.getter = defaultGetters(p, cc.Scale)
 
 	if err == nil {
@@ -59,9 +59,9 @@ func NewScriptProviderFromConfig(other map[string]interface{}) (Provider, error)
 	return p, err
 }
 
-// NewScriptProvider creates a script provider.
+// NewScriptProvider creates a script plugin.
 // Script execution is aborted after given timeout.
-func NewScriptProvider(script string, timeout time.Duration, scale float64, cache time.Duration) (*Script, error) {
+func NewScripPlugin(script string, timeout time.Duration, scale float64, cache time.Duration) (*Script, error) {
 	if strings.TrimSpace(script) == "" {
 		return nil, errors.New("script is required")
 	}
@@ -140,21 +140,21 @@ func scriptSetter[T any](p *Script, param string) (func(T) error, error) {
 	}, nil
 }
 
-var _ SetIntProvider = (*Script)(nil)
+var _ IntSetter = (*Script)(nil)
 
 // IntSetter invokes script with parameter replaced by int value
 func (p *Script) IntSetter(param string) (func(int64) error, error) {
 	return scriptSetter[int64](p, param)
 }
 
-var _ SetBoolProvider = (*Script)(nil)
+var _ BoolSetter = (*Script)(nil)
 
 // BoolSetter invokes script with parameter replaced by bool value
 func (p *Script) BoolSetter(param string) (func(bool) error, error) {
 	return scriptSetter[bool](p, param)
 }
 
-var _ SetStringProvider = (*Script)(nil)
+var _ StringSetter = (*Script)(nil)
 
 // StringSetter returns a function that invokes a script with parameter by a string value
 func (p *Script) StringSetter(param string) (func(string) error, error) {
diff --git a/provider/sequence.go b/plugin/sequence.go
similarity index 66%
rename from provider/sequence.go
rename to plugin/sequence.go
index 131b371c..151099f9 100644
--- a/provider/sequence.go
+++ b/plugin/sequence.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -6,7 +6,7 @@ import (
 	"github.com/evcc-io/evcc/util"
 )
 
-type sequenceProvider struct {
+type sequencePlugin struct {
 	ctx context.Context
 	set []Config
 }
@@ -16,7 +16,7 @@ func init() {
 }
 
 // NewSequenceFromConfig creates sequence provider
-func NewSequenceFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewSequenceFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Set []Config
 	}
@@ -25,7 +25,7 @@ func NewSequenceFromConfig(ctx context.Context, other map[string]interface{}) (P
 		return nil, err
 	}
 
-	o := &sequenceProvider{
+	o := &sequencePlugin{
 		ctx: ctx,
 		set: cc.Set,
 	}
@@ -33,12 +33,12 @@ func NewSequenceFromConfig(ctx context.Context, other map[string]interface{}) (P
 	return o, nil
 }
 
-var _ SetIntProvider = (*sequenceProvider)(nil)
+var _ IntSetter = (*sequencePlugin)(nil)
 
-func (o *sequenceProvider) IntSetter(param string) (func(int64) error, error) {
+func (o *sequencePlugin) IntSetter(param string) (func(int64) error, error) {
 	set := make([]func(int64) error, 0, len(o.set))
 	for _, cc := range o.set {
-		s, err := NewIntSetterFromConfig(o.ctx, param, cc)
+		s, err := cc.IntSetter(o.ctx, param)
 		if err != nil {
 			return nil, err
 		}
@@ -55,12 +55,12 @@ func (o *sequenceProvider) IntSetter(param string) (func(int64) error, error) {
 	}, nil
 }
 
-var _ SetFloatProvider = (*sequenceProvider)(nil)
+var _ FloatSetter = (*sequencePlugin)(nil)
 
-func (o *sequenceProvider) FloatSetter(param string) (func(float64) error, error) {
+func (o *sequencePlugin) FloatSetter(param string) (func(float64) error, error) {
 	set := make([]func(float64) error, 0, len(o.set))
 	for _, cc := range o.set {
-		s, err := NewFloatSetterFromConfig(o.ctx, param, cc)
+		s, err := cc.FloatSetter(o.ctx, param)
 		if err != nil {
 			return nil, err
 		}
@@ -77,12 +77,12 @@ func (o *sequenceProvider) FloatSetter(param string) (func(float64) error, error
 	}, nil
 }
 
-var _ SetBoolProvider = (*sequenceProvider)(nil)
+var _ BoolSetter = (*sequencePlugin)(nil)
 
-func (o *sequenceProvider) BoolSetter(param string) (func(bool) error, error) {
+func (o *sequencePlugin) BoolSetter(param string) (func(bool) error, error) {
 	set := make([]func(bool) error, 0, len(o.set))
 	for _, cc := range o.set {
-		s, err := NewBoolSetterFromConfig(o.ctx, param, cc)
+		s, err := cc.BoolSetter(o.ctx, param)
 		if err != nil {
 			return nil, err
 		}
diff --git a/provider/sma.go b/plugin/sma.go
similarity index 74%
rename from provider/sma.go
rename to plugin/sma.go
index 948de84a..4a139998 100644
--- a/provider/sma.go
+++ b/plugin/sma.go
@@ -1,10 +1,10 @@
-package provider
+package plugin
 
 import (
 	"errors"
 	"fmt"
 
-	"github.com/evcc-io/evcc/provider/sma"
+	"github.com/evcc-io/evcc/plugin/sma"
 	"github.com/evcc-io/evcc/util"
 	"gitlab.com/bboehmke/sunny"
 )
@@ -21,7 +21,7 @@ func init() {
 }
 
 // NewSMAFromConfig creates SMA provider
-func NewSMAFromConfig(other map[string]interface{}) (Provider, error) {
+func NewSMAFromConfig(other map[string]interface{}) (Plugin, error) {
 	cc := struct {
 		URI, Password, Interface string
 		Serial                   uint32
@@ -41,19 +41,19 @@ func NewSMAFromConfig(other map[string]interface{}) (Provider, error) {
 		return nil, fmt.Errorf("failed to get discoverer failed: %w", err)
 	}
 
-	provider := &SMA{
+	o := &SMA{
 		scale: cc.Scale,
 	}
 	switch {
 	case cc.URI != "":
-		provider.device, err = discoverer.DeviceByIP(cc.URI, cc.Password)
+		o.device, err = discoverer.DeviceByIP(cc.URI, cc.Password)
 		if err != nil {
 			return nil, err
 		}
 
 	case cc.Serial > 0:
-		provider.device = discoverer.DeviceBySerial(cc.Serial, cc.Password)
-		if provider.device == nil {
+		o.device = discoverer.DeviceBySerial(cc.Serial, cc.Password)
+		if o.device == nil {
 			return nil, fmt.Errorf("device not found: %d", cc.Serial)
 		}
 
@@ -61,15 +61,12 @@ func NewSMAFromConfig(other map[string]interface{}) (Provider, error) {
 		return nil, errors.New("missing uri or serial")
 	}
 
-	provider.value, err = sunny.ValueIDString(cc.Value)
-	if err != nil {
-		return nil, err
-	}
+	o.value, err = sunny.ValueIDString(cc.Value)
 
-	return provider, err
+	return o, err
 }
 
-var _ FloatProvider = (*SMA)(nil)
+var _ FloatGetter = (*SMA)(nil)
 
 // FloatGetter creates handler for float64
 func (p *SMA) FloatGetter() (func() (float64, error), error) {
@@ -83,7 +80,7 @@ func (p *SMA) FloatGetter() (func() (float64, error), error) {
 	}, nil
 }
 
-var _ IntProvider = (*SMA)(nil)
+var _ IntGetter = (*SMA)(nil)
 
 // IntGetter creates handler for int64
 func (p *SMA) IntGetter() (func() (int64, error), error) {
diff --git a/provider/sma/device.go b/plugin/sma/device.go
similarity index 100%
rename from provider/sma/device.go
rename to plugin/sma/device.go
diff --git a/provider/sma/discover.go b/plugin/sma/discover.go
similarity index 100%
rename from provider/sma/discover.go
rename to plugin/sma/discover.go
diff --git a/provider/socket.go b/plugin/socket.go
similarity index 90%
rename from provider/socket.go
rename to plugin/socket.go
index 5d7daebf..ea327855 100644
--- a/provider/socket.go
+++ b/plugin/socket.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -8,7 +8,7 @@ import (
 
 	"github.com/coder/websocket"
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider/pipeline"
+	"github.com/evcc-io/evcc/plugin/pipeline"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/transport"
@@ -28,12 +28,12 @@ type Socket struct {
 }
 
 func init() {
-	registry.Add("ws", NewSocketProviderFromConfig)
-	registry.Add("websocket", NewSocketProviderFromConfig)
+	registry.Add("ws", NewSocketPluginFromConfig)
+	registry.Add("websocket", NewSocketPluginFromConfig)
 }
 
-// NewSocketProviderFromConfig creates a HTTP provider
-func NewSocketProviderFromConfig(other map[string]interface{}) (Provider, error) {
+// NewSocketPluginFromConfig creates a HTTP provider
+func NewSocketPluginFromConfig(other map[string]interface{}) (Plugin, error) {
 	cc := struct {
 		URI               string
 		Headers           map[string]string
diff --git a/provider/sunspec.go b/plugin/sunspec.go
similarity index 96%
rename from provider/sunspec.go
rename to plugin/sunspec.go
index 30c27851..b956fac6 100644
--- a/provider/sunspec.go
+++ b/plugin/sunspec.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"errors"
@@ -28,7 +28,7 @@ func init() {
 }
 
 // NewModbusSunspecFromConfig creates Modbus plugin
-func NewModbusSunspecFromConfig(other map[string]interface{}) (Provider, error) {
+func NewModbusSunspecFromConfig(other map[string]interface{}) (Plugin, error) {
 	cc := struct {
 		modbus.Settings `mapstructure:",squash"`
 		Value           []string
@@ -137,14 +137,14 @@ func (m *ModbusSunspec) floatGetter() (f float64, err error) {
 	return m.scale * res, nil
 }
 
-var _ FloatProvider = (*Modbus)(nil)
+var _ FloatGetter = (*Modbus)(nil)
 
 // FloatGetter executes configured modbus read operation and implements func() (float64, error)
 func (m *ModbusSunspec) FloatGetter() (func() (f float64, err error), error) {
 	return m.floatGetter, nil
 }
 
-var _ IntProvider = (*Modbus)(nil)
+var _ IntGetter = (*Modbus)(nil)
 
 // IntGetter executes configured modbus read operation and implements IntProvider
 func (m *ModbusSunspec) IntGetter() (func() (int64, error), error) {
@@ -176,9 +176,9 @@ func (m *ModbusSunspec) blockPoint() (block sunspec.Block, point sunspec.Point,
 	return block, point, err
 }
 
-var _ SetFloatProvider = (*Modbus)(nil)
+var _ FloatSetter = (*Modbus)(nil)
 
-// FloatSetter executes configured modbus write operation and implements SetFloatProvider
+// FloatSetter executes configured modbus write operation and implements FloatSetter
 func (m *ModbusSunspec) FloatSetter(_ string) (func(float64) error, error) {
 	block, point, err := m.blockPoint()
 	if err != nil {
@@ -206,9 +206,9 @@ func (m *ModbusSunspec) FloatSetter(_ string) (func(float64) error, error) {
 	}, nil
 }
 
-var _ SetIntProvider = (*Modbus)(nil)
+var _ IntSetter = (*Modbus)(nil)
 
-// IntSetter executes configured modbus write operation and implements SetIntProvider
+// IntSetter executes configured modbus write operation and implements IntSetter
 func (m *ModbusSunspec) IntSetter(_ string) (func(int64) error, error) {
 	block, point, err := m.blockPoint()
 	if err != nil {
diff --git a/provider/sunspec_cache.go b/plugin/sunspec_cache.go
similarity index 98%
rename from provider/sunspec_cache.go
rename to plugin/sunspec_cache.go
index 8c53f169..dc2b8b50 100644
--- a/provider/sunspec_cache.go
+++ b/plugin/sunspec_cache.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"strconv"
diff --git a/provider/switch.go b/plugin/switch.go
similarity index 73%
rename from provider/switch.go
rename to plugin/switch.go
index e420850c..7ba482fe 100644
--- a/provider/switch.go
+++ b/plugin/switch.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -13,7 +13,7 @@ type Case struct {
 	Set  Config
 }
 
-type switchProvider struct {
+type switchPlugin struct {
 	ctx   context.Context
 	cases []Case
 	dflt  *Config
@@ -24,7 +24,7 @@ func init() {
 }
 
 // NewSwitchFromConfig creates switch provider
-func NewSwitchFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewSwitchFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Switch  []Case
 		Default *Config
@@ -42,7 +42,7 @@ func NewSwitchFromConfig(ctx context.Context, other map[string]interface{}) (Pro
 		cases[c.Case] = struct{}{}
 	}
 
-	o := &switchProvider{
+	o := &switchPlugin{
 		ctx:   ctx,
 		cases: cc.Switch,
 		dflt:  cc.Default,
@@ -51,24 +51,21 @@ func NewSwitchFromConfig(ctx context.Context, other map[string]interface{}) (Pro
 	return o, nil
 }
 
-var _ SetIntProvider = (*switchProvider)(nil)
+var _ IntSetter = (*switchPlugin)(nil)
 
-func (o *switchProvider) IntSetter(param string) (func(int64) error, error) {
+func (o *switchPlugin) IntSetter(param string) (func(int64) error, error) {
 	set := make([]func(int64) error, 0, len(o.cases))
 	for _, cc := range o.cases {
-		s, err := NewIntSetterFromConfig(o.ctx, param, cc.Set)
+		s, err := cc.Set.IntSetter(o.ctx, param)
 		if err != nil {
 			return nil, err
 		}
 		set = append(set, s)
 	}
 
-	var dflt func(int64) error
-	if o.dflt != nil {
-		var err error
-		if dflt, err = NewIntSetterFromConfig(o.ctx, param, *o.dflt); err != nil {
-			return nil, err
-		}
+	dflt, err := o.dflt.IntSetter(o.ctx, param)
+	if err != nil {
+		return nil, err
 	}
 
 	return func(val int64) error {
diff --git a/provider/timeseries.go b/plugin/timeseries.go
similarity index 88%
rename from provider/timeseries.go
rename to plugin/timeseries.go
index 38c09914..6274877e 100644
--- a/provider/timeseries.go
+++ b/plugin/timeseries.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -16,11 +16,11 @@ func init() {
 }
 
 // TimeSeriesFromConfig creates timeseries provider
-func TimeSeriesFromConfig(_ context.Context, _ map[string]interface{}) (Provider, error) {
+func TimeSeriesFromConfig(_ context.Context, _ map[string]interface{}) (Plugin, error) {
 	return new(timeseriesProvider), nil
 }
 
-var _ StringProvider = (*timeseriesProvider)(nil)
+var _ StringGetter = (*timeseriesProvider)(nil)
 
 func (p *timeseriesProvider) StringGetter() (func() (string, error), error) {
 	return func() (string, error) {
diff --git a/provider/transformation.go b/plugin/transformation.go
similarity index 86%
rename from provider/transformation.go
rename to plugin/transformation.go
index 8fd705e4..97f73236 100644
--- a/provider/transformation.go
+++ b/plugin/transformation.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -29,28 +29,28 @@ func configureInputs(ctx context.Context, inConfig []transformationConfig) ([]in
 
 		switch strings.ToLower(cc.Type) {
 		case "bool":
-			ff, err := NewBoolGetterFromConfig(ctx, cc.Config)
+			ff, err := cc.Config.BoolGetter(ctx)
 			if err != nil {
 				return nil, fmt.Errorf("%s: %w", cc.Name, err)
 			}
 			f = func() (any, error) { return ff() }
 
 		case "int":
-			ff, err := NewIntGetterFromConfig(ctx, cc.Config)
+			ff, err := cc.Config.IntGetter(ctx)
 			if err != nil {
 				return nil, fmt.Errorf("%s: %w", cc.Name, err)
 			}
 			f = func() (any, error) { return ff() }
 
 		case "float":
-			ff, err := NewFloatGetterFromConfig(ctx, cc.Config)
+			ff, err := cc.Config.FloatGetter(ctx)
 			if err != nil {
 				return nil, fmt.Errorf("%s: %w", cc.Name, err)
 			}
 			f = func() (any, error) { return ff() }
 
 		case "string":
-			ff, err := NewStringGetterFromConfig(ctx, cc.Config)
+			ff, err := cc.Config.StringGetter(ctx)
 			if err != nil {
 				return nil, fmt.Errorf("%s: %w", cc.Name, err)
 			}
@@ -76,7 +76,7 @@ func configureOutputs(ctx context.Context, outConfig []transformationConfig) ([]
 
 		switch strings.ToLower(cc.Type) {
 		case "bool":
-			ff, err := NewBoolSetterFromConfig(ctx, cc.Name, cc.Config)
+			ff, err := cc.Config.BoolSetter(ctx, cc.Name)
 			if err != nil {
 				return nil, fmt.Errorf("%s: %w", cc.Name, err)
 			}
@@ -86,7 +86,7 @@ func configureOutputs(ctx context.Context, outConfig []transformationConfig) ([]
 			}
 
 		case "int":
-			ff, err := NewIntSetterFromConfig(ctx, cc.Name, cc.Config)
+			ff, err := cc.Config.IntSetter(ctx, cc.Name)
 			if err != nil {
 				return nil, fmt.Errorf("%s: %w", cc.Name, err)
 			}
@@ -96,7 +96,7 @@ func configureOutputs(ctx context.Context, outConfig []transformationConfig) ([]
 			}
 
 		case "float":
-			ff, err := NewFloatSetterFromConfig(ctx, cc.Name, cc.Config)
+			ff, err := cc.Config.FloatSetter(ctx, cc.Name)
 			if err != nil {
 				return nil, fmt.Errorf("%s: %w", cc.Name, err)
 			}
@@ -106,7 +106,7 @@ func configureOutputs(ctx context.Context, outConfig []transformationConfig) ([]
 			}
 
 		case "string":
-			ff, err := NewStringSetterFromConfig(ctx, cc.Name, cc.Config)
+			ff, err := cc.Config.StringSetter(ctx, cc.Name)
 			if err != nil {
 				return nil, fmt.Errorf("%s: %w", cc.Name, err)
 			}
diff --git a/provider/watchdog.go b/plugin/watchdog.go
similarity index 69%
rename from provider/watchdog.go
rename to plugin/watchdog.go
index eae4016c..f7e3dd32 100644
--- a/provider/watchdog.go
+++ b/plugin/watchdog.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -9,7 +9,7 @@ import (
 	"github.com/evcc-io/evcc/util"
 )
 
-type watchdogProvider struct {
+type watchdogPlugin struct {
 	mu      sync.Mutex
 	ctx     context.Context
 	log     *util.Logger
@@ -24,7 +24,7 @@ func init() {
 }
 
 // NewWatchDogFromConfig creates watchDog provider
-func NewWatchDogFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
+func NewWatchDogFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
 	var cc struct {
 		Reset   *string
 		Set     Config
@@ -35,7 +35,7 @@ func NewWatchDogFromConfig(ctx context.Context, other map[string]interface{}) (P
 		return nil, err
 	}
 
-	o := &watchdogProvider{
+	o := &watchdogPlugin{
 		ctx:     ctx,
 		log:     contextLogger(ctx, util.NewLogger("watchdog")),
 		reset:   cc.Reset,
@@ -46,7 +46,7 @@ func NewWatchDogFromConfig(ctx context.Context, other map[string]interface{}) (P
 	return o, nil
 }
 
-func (o *watchdogProvider) wdt(ctx context.Context, set func() error) {
+func (o *watchdogPlugin) wdt(ctx context.Context, set func() error) {
 	for tick := time.Tick(o.timeout); ; {
 		select {
 		case <-tick:
@@ -59,9 +59,9 @@ func (o *watchdogProvider) wdt(ctx context.Context, set func() error) {
 	}
 }
 
-// setter is the generic setter function for watchdogProvider
+// setter is the generic setter function for watchdogPlugin
 // it is currently not possible to write this as a method
-func setter[T comparable](o *watchdogProvider, set func(T) error, reset *T) func(T) error {
+func setter[T comparable](o *watchdogPlugin, set func(T) error, reset *T) func(T) error {
 	return func(val T) error {
 		o.mu.Lock()
 
@@ -87,10 +87,10 @@ func setter[T comparable](o *watchdogProvider, set func(T) error, reset *T) func
 	}
 }
 
-var _ SetIntProvider = (*watchdogProvider)(nil)
+var _ IntSetter = (*watchdogPlugin)(nil)
 
-func (o *watchdogProvider) IntSetter(param string) (func(int64) error, error) {
-	set, err := NewIntSetterFromConfig(o.ctx, param, o.set)
+func (o *watchdogPlugin) IntSetter(param string) (func(int64) error, error) {
+	set, err := o.set.IntSetter(o.ctx, param)
 	if err != nil {
 		return nil, err
 	}
@@ -107,10 +107,10 @@ func (o *watchdogProvider) IntSetter(param string) (func(int64) error, error) {
 	return setter(o, set, reset), nil
 }
 
-var _ SetFloatProvider = (*watchdogProvider)(nil)
+var _ FloatSetter = (*watchdogPlugin)(nil)
 
-func (o *watchdogProvider) FloatSetter(param string) (func(float64) error, error) {
-	set, err := NewFloatSetterFromConfig(o.ctx, param, o.set)
+func (o *watchdogPlugin) FloatSetter(param string) (func(float64) error, error) {
+	set, err := o.set.FloatSetter(o.ctx, param)
 	if err != nil {
 		return nil, err
 	}
@@ -127,10 +127,10 @@ func (o *watchdogProvider) FloatSetter(param string) (func(float64) error, error
 	return setter(o, set, reset), nil
 }
 
-var _ SetBoolProvider = (*watchdogProvider)(nil)
+var _ BoolSetter = (*watchdogPlugin)(nil)
 
-func (o *watchdogProvider) BoolSetter(param string) (func(bool) error, error) {
-	set, err := NewBoolSetterFromConfig(o.ctx, param, o.set)
+func (o *watchdogPlugin) BoolSetter(param string) (func(bool) error, error) {
+	set, err := o.set.BoolSetter(o.ctx, param)
 	if err != nil {
 		return nil, err
 	}
diff --git a/provider/config.go b/provider/config.go
deleted file mode 100644
index ee71706f..00000000
--- a/provider/config.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package provider
-
-import (
-	"context"
-	"fmt"
-
-	reg "github.com/evcc-io/evcc/util/registry"
-)
-
-var registry = reg.New[Provider]("plugin")
-
-// provider types
-type (
-	Provider interface{}
-	Getters  interface {
-		StringProvider
-		FloatProvider
-		IntProvider
-		BoolProvider
-	}
-	StringProvider interface {
-		StringGetter() (func() (string, error), error)
-	}
-	FloatProvider interface {
-		FloatGetter() (func() (float64, error), error)
-	}
-	IntProvider interface {
-		IntGetter() (func() (int64, error), error)
-	}
-	BoolProvider interface {
-		BoolGetter() (func() (bool, error), error)
-	}
-	SetStringProvider interface {
-		StringSetter(param string) (func(string) error, error)
-	}
-	SetFloatProvider interface {
-		FloatSetter(param string) (func(float64) error, error)
-	}
-	SetIntProvider interface {
-		IntSetter(param string) (func(int64) error, error)
-	}
-	SetBoolProvider interface {
-		BoolSetter(param string) (func(bool) error, error)
-	}
-	SetBytesProvider interface {
-		BytesSetter(param string) (func([]byte) error, error)
-	}
-)
-
-// Config is the general provider config
-type Config struct {
-	Source string
-	Other  map[string]any `mapstructure:",remain" yaml:",inline"`
-}
-
-func provider[T any](typ string, ctx context.Context, config Config) (T, error) {
-	var zero T
-
-	factory, err := registry.Get(config.Source)
-	if err != nil {
-		return zero, err
-	}
-
-	provider, err := factory(ctx, config.Other)
-	if err != nil {
-		return zero, err
-	}
-
-	prov, ok := provider.(T)
-	if !ok {
-		return zero, fmt.Errorf("invalid plugin source for type %s: %s", typ, config.Source)
-	}
-
-	return prov, nil
-}
-
-// NewIntGetterFromConfig creates a IntGetter from config
-func NewIntGetterFromConfig(ctx context.Context, config Config) (func() (int64, error), error) {
-	prov, err := provider[IntProvider]("int", ctx, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return prov.IntGetter()
-}
-
-// NewFloatGetterFromConfig creates a FloatGetter from config
-func NewFloatGetterFromConfig(ctx context.Context, config Config) (func() (float64, error), error) {
-	prov, err := provider[FloatProvider]("float", ctx, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return prov.FloatGetter()
-}
-
-// NewStringGetterFromConfig creates a StringGetter from config
-func NewStringGetterFromConfig(ctx context.Context, config Config) (func() (string, error), error) {
-	prov, err := provider[StringProvider]("string", ctx, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return prov.StringGetter()
-}
-
-// NewBoolGetterFromConfig creates a BoolGetter from config
-func NewBoolGetterFromConfig(ctx context.Context, config Config) (func() (bool, error), error) {
-	prov, err := provider[BoolProvider]("bool", ctx, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return prov.BoolGetter()
-}
-
-// NewIntSetterFromConfig creates a IntSetter from config
-func NewIntSetterFromConfig(ctx context.Context, param string, config Config) (func(int64) error, error) {
-	prov, err := provider[SetIntProvider]("int", ctx, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return prov.IntSetter(param)
-}
-
-// NewFloatSetterFromConfig creates a FloatSetter from config
-func NewFloatSetterFromConfig(ctx context.Context, param string, config Config) (func(float642 float64) error, error) {
-	prov, err := provider[SetFloatProvider]("float", ctx, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return prov.FloatSetter(param)
-}
-
-// NewStringSetterFromConfig creates a StringSetter from config
-func NewStringSetterFromConfig(ctx context.Context, param string, config Config) (func(string) error, error) {
-	prov, err := provider[SetStringProvider]("string", ctx, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return prov.StringSetter(param)
-}
-
-// NewBoolSetterFromConfig creates a BoolSetter from config
-func NewBoolSetterFromConfig(ctx context.Context, param string, config Config) (func(bool) error, error) {
-	prov, err := provider[SetBoolProvider]("bool", ctx, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return prov.BoolSetter(param)
-}
-
-// NewBytesSetterFromConfig creates a BytesSetter from config
-func NewBytesSetterFromConfig(ctx context.Context, param string, config Config) (func([]byte) error, error) {
-	prov, err := provider[SetBytesProvider]("bytes", ctx, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return prov.BytesSetter(param)
-}
diff --git a/push/push.go b/push/push.go
index f3dc8105..e5949fe2 100644
--- a/push/push.go
+++ b/push/push.go
@@ -8,7 +8,7 @@ import (
 	"strings"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -19,7 +19,7 @@ func init() {
 // NewConfigurableFromConfig creates Messenger from config
 func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}) (Messenger, error) {
 	var cc struct {
-		Send     provider.Config
+		Send     plugin.Config
 		Encoding string
 	}
 
@@ -27,7 +27,7 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 		return nil, err
 	}
 
-	send, err := provider.NewStringSetterFromConfig(ctx, "send", cc.Send)
+	send, err := cc.Send.StringSetter(ctx, "send")
 	if err != nil {
 		return nil, err
 	}
diff --git a/server/mqtt.go b/server/mqtt.go
index f36f9d57..fae7a971 100644
--- a/server/mqtt.go
+++ b/server/mqtt.go
@@ -12,7 +12,7 @@ import (
 	"github.com/evcc-io/evcc/core/loadpoint"
 	"github.com/evcc-io/evcc/core/site"
 	"github.com/evcc-io/evcc/core/vehicle"
-	"github.com/evcc-io/evcc/provider/mqtt"
+	"github.com/evcc-io/evcc/plugin/mqtt"
 	"github.com/evcc-io/evcc/util"
 )
 
diff --git a/tariff/embed.go b/tariff/embed.go
index effc0213..9f050654 100644
--- a/tariff/embed.go
+++ b/tariff/embed.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"time"
 
-	"github.com/evcc-io/evcc/provider/golang/stdlib"
+	"github.com/evcc-io/evcc/plugin/golang/stdlib"
 	"github.com/traefik/yaegi/interp"
 )
 
diff --git a/tariff/tariff.go b/tariff/tariff.go
index 2cbaec8a..b1f08208 100644
--- a/tariff/tariff.go
+++ b/tariff/tariff.go
@@ -10,7 +10,7 @@ import (
 
 	"github.com/cenkalti/backoff/v4"
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/jinzhu/now"
 )
@@ -32,8 +32,8 @@ func init() {
 func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}) (api.Tariff, error) {
 	cc := struct {
 		embed    `mapstructure:",squash"`
-		Price    *provider.Config
-		Forecast *provider.Config
+		Price    *plugin.Config
+		Forecast *plugin.Config
 		Type     api.TariffType `mapstructure:"tariff"`
 		Cache    time.Duration
 	}{
@@ -52,26 +52,17 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 		return nil, err
 	}
 
-	var (
-		err       error
-		priceG    func() (float64, error)
-		forecastG func() (string, error)
-	)
-
-	if cc.Price != nil {
-		priceG, err = provider.NewFloatGetterFromConfig(ctx, *cc.Price)
-		if err != nil {
-			return nil, fmt.Errorf("price: %w", err)
-		}
-
-		priceG = provider.Cached(priceG, cc.Cache)
+	priceG, err := cc.Price.FloatGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("price: %w", err)
+	}
+	if priceG != nil {
+		priceG = plugin.Cached(priceG, cc.Cache)
 	}
 
-	if cc.Forecast != nil {
-		forecastG, err = provider.NewStringGetterFromConfig(ctx, *cc.Forecast)
-		if err != nil {
-			return nil, fmt.Errorf("forecast: %w", err)
-		}
+	forecastG, err := cc.Forecast.StringGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("forecast: %w", err)
 	}
 
 	t := &Tariff{
diff --git a/vehicle/aiways/provider.go b/vehicle/aiways/provider.go
index d0662df3..8ee70594 100644
--- a/vehicle/aiways/provider.go
+++ b/vehicle/aiways/provider.go
@@ -5,7 +5,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 // Provider implements the vehicle api
@@ -16,7 +16,7 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, user int64, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (StatusResponse, error) {
+		statusG: plugin.Cached(func() (StatusResponse, error) {
 			return api.Status(user, vin)
 		}, cache),
 	}
diff --git a/vehicle/bluelink/provider.go b/vehicle/bluelink/provider.go
index 689f0d96..b0cf617d 100644
--- a/vehicle/bluelink/provider.go
+++ b/vehicle/bluelink/provider.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 const refreshTimeout = 2 * time.Minute
@@ -28,13 +28,13 @@ func NewProvider(api *API, vehicle Vehicle, expiry, cache time.Duration) *Provid
 		expiry: expiry,
 	}
 
-	v.statusG = provider.Cached(func() (BluelinkVehicleStatus, error) {
+	v.statusG = plugin.Cached(func() (BluelinkVehicleStatus, error) {
 		return v.status(
 			func() (BluelinkVehicleStatusLatest, error) { return api.StatusLatest(vehicle) },
 		)
 	}, cache)
 
-	v.statusLG = provider.Cached(func() (BluelinkVehicleStatusLatest, error) {
+	v.statusLG = plugin.Cached(func() (BluelinkVehicleStatusLatest, error) {
 		return api.StatusLatest(vehicle)
 	}, cache)
 
diff --git a/vehicle/bmw/provider.go b/vehicle/bmw/provider.go
index 3c65beee..57c88599 100644
--- a/vehicle/bmw/provider.go
+++ b/vehicle/bmw/provider.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 // Provider implements the vehicle api
@@ -16,7 +16,7 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (VehicleStatus, error) {
+		statusG: plugin.Cached(func() (VehicleStatus, error) {
 			return api.Status(vin)
 		}, cache),
 		actionS: func(action string) error {
diff --git a/vehicle/carwings.go b/vehicle/carwings.go
index a088a66f..ab99924f 100644
--- a/vehicle/carwings.go
+++ b/vehicle/carwings.go
@@ -8,7 +8,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/joeshaw/carwings"
@@ -90,8 +90,8 @@ func NewCarWingsFromConfig(other map[string]interface{}) (api.Vehicle, error) {
 		return nil, fmt.Errorf("login failed: %w", err)
 	}
 
-	v.statusG = provider.Cached(v.status, cc.Cache)
-	v.climateG = provider.Cached(v.session.ClimateControlStatus, cc.Cache)
+	v.statusG = plugin.Cached(v.status, cc.Cache)
+	v.climateG = plugin.Cached(v.session.ClimateControlStatus, cc.Cache)
 
 	return v, nil
 }
diff --git a/vehicle/cloud.go b/vehicle/cloud.go
index 97e7d800..3e47db7d 100644
--- a/vehicle/cloud.go
+++ b/vehicle/cloud.go
@@ -7,7 +7,7 @@ import (
 
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/api/proto/pb"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/cloud"
 	"github.com/evcc-io/evcc/util/request"
@@ -63,7 +63,7 @@ func NewCloudFromConfig(other map[string]interface{}) (api.Vehicle, error) {
 
 	err = v.prepareVehicle()
 
-	v.chargeStateG = provider.Cached(v.chargeState, cc.Cache)
+	v.chargeStateG = plugin.Cached(v.chargeState, cc.Cache)
 
 	return v, err
 }
diff --git a/vehicle/fiat/provider.go b/vehicle/fiat/provider.go
index 3cf85bde..9672ac74 100644
--- a/vehicle/fiat/provider.go
+++ b/vehicle/fiat/provider.go
@@ -7,7 +7,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util/request"
 )
 
@@ -23,10 +23,10 @@ type Provider struct {
 
 func NewProvider(api *API, vin, pin string, expiry, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (StatusResponse, error) {
+		statusG: plugin.Cached(func() (StatusResponse, error) {
 			return api.Status(vin)
 		}, cache),
-		locationG: provider.Cached(func() (LocationResponse, error) {
+		locationG: plugin.Cached(func() (LocationResponse, error) {
 			return api.Location(vin)
 		}, cache),
 		action: func(action, cmd string) (ActionResponse, error) {
@@ -37,7 +37,7 @@ func NewProvider(api *API, vin, pin string, expiry, cache time.Duration) *Provid
 
 	// use pin for refreshing
 	if pin != "" {
-		impl.statusG = provider.Cached(func() (StatusResponse, error) {
+		impl.statusG = plugin.Cached(func() (StatusResponse, error) {
 			return impl.status(
 				func() (StatusResponse, error) { return api.Status(vin) },
 			)
diff --git a/vehicle/ford/connect/provider.go b/vehicle/ford/connect/provider.go
index 2351ad1d..61e822c4 100644
--- a/vehicle/ford/connect/provider.go
+++ b/vehicle/ford/connect/provider.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 type Provider struct {
@@ -14,7 +14,7 @@ type Provider struct {
 
 func NewProvider(api *API, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (Vehicle, error) {
+		statusG: plugin.Cached(func() (Vehicle, error) {
 			return api.Status(vin)
 		}, cache),
 		// refreshG: func() error {
diff --git a/vehicle/ford/provider.go b/vehicle/ford/provider.go
index 7718bf91..7f9e9173 100644
--- a/vehicle/ford/provider.go
+++ b/vehicle/ford/provider.go
@@ -5,7 +5,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/vehicle/ford/autonomic"
 )
 
@@ -16,7 +16,7 @@ type Provider struct {
 
 func NewProvider(api *autonomic.API, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (autonomic.MetricsResponse, error) {
+		statusG: plugin.Cached(func() (autonomic.MetricsResponse, error) {
 			return api.Status(vin)
 		}, cache),
 		refreshG: func() error {
diff --git a/vehicle/jlr/provider.go b/vehicle/jlr/provider.go
index 42fb1814..f5b13fb2 100644
--- a/vehicle/jlr/provider.go
+++ b/vehicle/jlr/provider.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 type Provider struct {
@@ -15,10 +15,10 @@ type Provider struct {
 
 func NewProvider(api *API, vin, user string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (StatusResponse, error) {
+		statusG: plugin.Cached(func() (StatusResponse, error) {
 			return api.Status(vin)
 		}, cache),
-		positionG: provider.Cached(func() (PositionResponse, error) {
+		positionG: plugin.Cached(func() (PositionResponse, error) {
 			return api.Position(vin)
 		}, cache),
 		chargeS: func(enable bool) error {
diff --git a/vehicle/mercedes/provider.go b/vehicle/mercedes/provider.go
index 2898d2a0..b7482089 100644
--- a/vehicle/mercedes/provider.go
+++ b/vehicle/mercedes/provider.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 type Provider struct {
@@ -13,7 +13,7 @@ type Provider struct {
 
 func NewProvider(api *API, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		dataG: provider.Cached(func() (StatusResponse, error) {
+		dataG: plugin.Cached(func() (StatusResponse, error) {
 			return api.Status(vin)
 		}, cache),
 	}
@@ -115,11 +115,9 @@ func (v *Provider) FinishTime() (time.Time, error) {
 
 func MapChargeStatus(lookup int) api.ChargeStatus {
 	switch lookup {
-	case
-		0, 5, 6, 9, 10, 11:
+	case 0, 5, 6, 9, 10, 11:
 		return api.StatusC
-	case
-		1, 2, 4, 7, 8:
+	case 1, 2, 4, 7, 8:
 		return api.StatusB
 	}
 	return api.StatusA
diff --git a/vehicle/nissan/provider.go b/vehicle/nissan/provider.go
index d17b95ed..00e54cf5 100644
--- a/vehicle/nissan/provider.go
+++ b/vehicle/nissan/provider.go
@@ -5,7 +5,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 const refreshTimeout = 2 * time.Minute
@@ -28,7 +28,7 @@ func NewProvider(api *API, vin, version string, expiry, cache time.Duration) *Pr
 		expiry: expiry,
 	}
 
-	impl.statusG = provider.Cached(func() (StatusResponse, error) {
+	impl.statusG = plugin.Cached(func() (StatusResponse, error) {
 		return impl.status(
 			func() (StatusResponse, error) { return api.BatteryStatus(vin, version) },
 			func() (ActionResponse, error) { return api.RefreshRequest(vin, "RefreshBatteryStatus") },
diff --git a/vehicle/niu.go b/vehicle/niu.go
index f03c51f7..e7da3b80 100644
--- a/vehicle/niu.go
+++ b/vehicle/niu.go
@@ -10,7 +10,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/vehicle/niu"
@@ -62,7 +62,7 @@ func NewNiuFromConfig(other map[string]interface{}) (api.Vehicle, error) {
 		serial:   strings.ToUpper(cc.Serial),
 	}
 
-	v.apiG = provider.Cached(v.batteryAPI, cc.Cache)
+	v.apiG = plugin.Cached(v.batteryAPI, cc.Cache)
 
 	return v, nil
 }
diff --git a/vehicle/ovms.go b/vehicle/ovms.go
index 9a292e45..3ea15d2d 100644
--- a/vehicle/ovms.go
+++ b/vehicle/ovms.go
@@ -9,7 +9,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/vehicle/ovms"
@@ -59,9 +59,9 @@ func NewOvmsFromConfig(other map[string]interface{}) (api.Vehicle, error) {
 		cache:     cc.Cache,
 	}
 
-	v.chargeG = provider.Cached(v.batteryAPI, cc.Cache)
-	v.statusG = provider.Cached(v.statusAPI, cc.Cache)
-	v.locationG = provider.Cached(v.locationAPI, cc.Cache)
+	v.chargeG = plugin.Cached(v.batteryAPI, cc.Cache)
+	v.statusG = plugin.Cached(v.statusAPI, cc.Cache)
+	v.locationG = plugin.Cached(v.locationAPI, cc.Cache)
 
 	v.Jar, _ = cookiejar.New(&cookiejar.Options{
 		PublicSuffixList: publicsuffix.List,
diff --git a/vehicle/polestar/provider.go b/vehicle/polestar/provider.go
index 5b71def7..aa01bde4 100644
--- a/vehicle/polestar/provider.go
+++ b/vehicle/polestar/provider.go
@@ -5,7 +5,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -16,12 +16,12 @@ type Provider struct {
 
 func NewProvider(log *util.Logger, api *API, vin string, timeout, cache time.Duration) *Provider {
 	v := &Provider{
-		statusG: provider.Cached(func() (BatteryData, error) {
+		statusG: plugin.Cached(func() (BatteryData, error) {
 			ctx, cancel := context.WithTimeout(context.Background(), timeout)
 			defer cancel()
 			return api.Status(ctx, vin)
 		}, cache),
-		odoG: provider.Cached(func() (OdometerData, error) {
+		odoG: plugin.Cached(func() (OdometerData, error) {
 			ctx, cancel := context.WithTimeout(context.Background(), timeout)
 			defer cancel()
 			return api.Odometer(ctx, vin)
@@ -64,7 +64,7 @@ func (v *Provider) Range() (int64, error) {
 
 var _ api.VehicleOdometer = (*Provider)(nil)
 
-// Odometer implements the Provider.VehicleOdometer interface
+// Odometer implements the api.VehicleOdometer interface
 func (v *Provider) Odometer() (float64, error) {
 	res, err := v.odoG()
 	return res.OdometerMeters / 1e3, err
diff --git a/vehicle/porsche/provider.go b/vehicle/porsche/provider.go
index 577c5c22..a58b822b 100644
--- a/vehicle/porsche/provider.go
+++ b/vehicle/porsche/provider.go
@@ -5,7 +5,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -19,11 +19,11 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(log *util.Logger, connect *API, emobility *EmobilityAPI, vin, carModel string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (StatusResponse, error) {
+		statusG: plugin.Cached(func() (StatusResponse, error) {
 			return connect.Status(vin)
 		}, cache),
 
-		emobilityG: provider.Cached(func() (EmobilityResponse, error) {
+		emobilityG: plugin.Cached(func() (EmobilityResponse, error) {
 			if carModel != "" {
 				return emobility.Status(vin, carModel)
 			}
diff --git a/vehicle/psa/provider.go b/vehicle/psa/provider.go
index e31f5b86..38ab9ed2 100644
--- a/vehicle/psa/provider.go
+++ b/vehicle/psa/provider.go
@@ -5,7 +5,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 // Provider is an api.Vehicle implementation for PSA cars
@@ -16,7 +16,7 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, vid string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (Status, error) {
+		statusG: plugin.Cached(func() (Status, error) {
 			return api.Status(vid)
 		}, cache),
 	}
diff --git a/vehicle/renault/provider.go b/vehicle/renault/provider.go
index d92de41f..3e4194bd 100644
--- a/vehicle/renault/provider.go
+++ b/vehicle/renault/provider.go
@@ -7,7 +7,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/vehicle/renault/kamereon"
 )
@@ -25,13 +25,13 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(api *kamereon.API, accountID, vin string, alternativeWakeup bool, cache time.Duration) *Provider {
 	impl := &Provider{
-		batteryG: provider.Cached(func() (kamereon.Response, error) {
+		batteryG: plugin.Cached(func() (kamereon.Response, error) {
 			return api.Battery(accountID, vin)
 		}, cache),
-		cockpitG: provider.Cached(func() (kamereon.Response, error) {
+		cockpitG: plugin.Cached(func() (kamereon.Response, error) {
 			return api.Cockpit(accountID, vin)
 		}, cache),
-		hvacG: provider.Cached(func() (kamereon.Response, error) {
+		hvacG: plugin.Cached(func() (kamereon.Response, error) {
 			return api.Hvac(accountID, vin)
 		}, cache),
 		wakeup: func() (kamereon.Response, error) {
diff --git a/vehicle/saic/provider.go b/vehicle/saic/provider.go
index 1074133e..0348745b 100644
--- a/vehicle/saic/provider.go
+++ b/vehicle/saic/provider.go
@@ -5,7 +5,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/vehicle/saic/requests"
 )
 
@@ -25,14 +25,14 @@ var TargetSocVals = [...]int{0, 40, 50, 60, 70, 80, 90, 100}
 
 // Provider implements the vehicle api
 type Provider struct {
-	status provider.Cacheable[requests.ChargeStatus]
+	status plugin.Cacheable[requests.ChargeStatus]
 	wakeup func() error
 }
 
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		status: provider.ResettableCached(func() (requests.ChargeStatus, error) {
+		status: plugin.ResettableCached(func() (requests.ChargeStatus, error) {
 			return api.Status(vin)
 		}, cache),
 		wakeup: func() error {
@@ -54,7 +54,7 @@ func (v *Provider) Soc() (float64, error) {
 
 	val := res.ChrgMgmtData.BmsPackSOCDsp
 	if val > 1000 {
-		//v.status.Reset()
+		// v.status.Reset()
 		return float64(val), fmt.Errorf("invalid raw soc value: %d: %w", val, api.ErrMustRetry)
 	}
 
diff --git a/vehicle/seat/cupra/provider.go b/vehicle/seat/cupra/provider.go
index 9bdf5ea9..234d1551 100644
--- a/vehicle/seat/cupra/provider.go
+++ b/vehicle/seat/cupra/provider.go
@@ -5,7 +5,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 // Provider is an api.Vehicle implementation for Seat Cupra cars
@@ -19,13 +19,13 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, userID, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (Status, error) {
+		statusG: plugin.Cached(func() (Status, error) {
 			return api.Status(userID, vin)
 		}, cache),
-		positionG: provider.Cached(func() (Position, error) {
+		positionG: plugin.Cached(func() (Position, error) {
 			return api.ParkingPosition(vin)
 		}, cache),
-		milageG: provider.Cached(func() (Mileage, error) {
+		milageG: plugin.Cached(func() (Mileage, error) {
 			return api.Mileage(vin)
 		}, cache),
 		action: func(action, cmd string) error {
diff --git a/vehicle/skoda/myskoda/provider.go b/vehicle/skoda/myskoda/provider.go
index f3ad10c7..3a9982b4 100644
--- a/vehicle/skoda/myskoda/provider.go
+++ b/vehicle/skoda/myskoda/provider.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 // Provider implements the vehicle api
@@ -20,16 +20,16 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (StatusResponse, error) {
+		statusG: plugin.Cached(func() (StatusResponse, error) {
 			return api.Status(vin)
 		}, cache),
-		chargerG: provider.Cached(func() (ChargerResponse, error) {
+		chargerG: plugin.Cached(func() (ChargerResponse, error) {
 			return api.Charger(vin)
 		}, cache),
-		climateG: provider.Cached(func() (ClimaterResponse, error) {
+		climateG: plugin.Cached(func() (ClimaterResponse, error) {
 			return api.Climater(vin)
 		}, cache),
-		settingsG: provider.Cached(func() (SettingsResponse, error) {
+		settingsG: plugin.Cached(func() (SettingsResponse, error) {
 			return api.Settings(vin)
 		}, cache),
 		action: func(action, value string) error {
diff --git a/vehicle/skoda/provider.go b/vehicle/skoda/provider.go
index 920ac8ac..2ada52e8 100644
--- a/vehicle/skoda/provider.go
+++ b/vehicle/skoda/provider.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 // Provider implements the vehicle api
@@ -18,16 +18,16 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (StatusResponse, error) {
+		statusG: plugin.Cached(func() (StatusResponse, error) {
 			return api.Status(vin)
 		}, cache),
-		chargerG: provider.Cached(func() (ChargerResponse, error) {
+		chargerG: plugin.Cached(func() (ChargerResponse, error) {
 			return api.Charger(vin)
 		}, cache),
-		// climateG: provider.Cached(func() (interface{}, error) {
+		// climateG: plugin.Cached(func() (interface{}, error) {
 		// 	return api.Climater(vin)
 		// }, cache),
-		settingsG: provider.Cached(func() (SettingsResponse, error) {
+		settingsG: plugin.Cached(func() (SettingsResponse, error) {
 			return api.Settings(vin)
 		}, cache),
 		action: func(action, value string) error {
diff --git a/vehicle/smart/hello/provider.go b/vehicle/smart/hello/provider.go
index 36abcfcd..d9232351 100644
--- a/vehicle/smart/hello/provider.go
+++ b/vehicle/smart/hello/provider.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -16,7 +16,7 @@ type Provider struct {
 
 func NewProvider(log *util.Logger, api *API, vin string, cache time.Duration) *Provider {
 	v := &Provider{
-		statusG: provider.Cached(func() (VehicleStatus, error) {
+		statusG: plugin.Cached(func() (VehicleStatus, error) {
 			return api.Status(vin)
 		}, cache),
 	}
@@ -64,7 +64,7 @@ func (v *Provider) Position() (float64, float64, error) {
 
 var _ api.VehicleOdometer = (*Provider)(nil)
 
-// Odometer implements the Provider.VehicleOdometer interface
+// Odometer implements the api.VehicleOdometer interface
 func (v *Provider) Odometer() (float64, error) {
 	res, err := v.statusG()
 	return res.AdditionalVehicleStatus.MaintenanceStatus.Odometer, err
diff --git a/vehicle/smart/provider.go b/vehicle/smart/provider.go
index 20a1e776..716f78ca 100644
--- a/vehicle/smart/provider.go
+++ b/vehicle/smart/provider.go
@@ -5,7 +5,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -21,7 +21,7 @@ func NewProvider(log *util.Logger, api *API, vin string, expiry, cache time.Dura
 		expiry: expiry,
 	}
 
-	v.statusG = provider.Cached(func() (StatusResponse, error) {
+	v.statusG = plugin.Cached(func() (StatusResponse, error) {
 		return v.status(
 			func() (StatusResponse, error) { return api.Status(vin) },
 			func() (StatusResponse, error) { return api.Refresh(vin) },
@@ -93,7 +93,7 @@ func (v *Provider) Range() (int64, error) {
 
 var _ api.VehicleOdometer = (*Provider)(nil)
 
-// Odometer implements the Provider.VehicleOdometer interface
+// Odometer implements the api.VehicleOdometer interface
 func (v *Provider) Odometer() (float64, error) {
 	res, err := v.statusG()
 	return res.Status.Data.Odo.Value, err
diff --git a/vehicle/tesla/provider.go b/vehicle/tesla/provider.go
index df491667..293dc5a7 100644
--- a/vehicle/tesla/provider.go
+++ b/vehicle/tesla/provider.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/tesla-proxy-client"
 )
 
@@ -15,7 +15,7 @@ type Provider struct {
 
 func NewProvider(vehicle *tesla.Vehicle, cache time.Duration) *Provider {
 	impl := &Provider{
-		dataG: provider.Cached(func() (*tesla.VehicleData, error) {
+		dataG: plugin.Cached(func() (*tesla.VehicleData, error) {
 			res, err := vehicle.Data()
 			return res, apiError(err)
 		}, cache),
diff --git a/vehicle/tronity.go b/vehicle/tronity.go
index 323f4c51..d584703b 100644
--- a/vehicle/tronity.go
+++ b/vehicle/tronity.go
@@ -26,7 +26,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/oauth"
 	"github.com/evcc-io/evcc/util/request"
@@ -120,7 +120,7 @@ func NewTronityFromConfig(other map[string]interface{}) (api.Vehicle, error) {
 	}
 
 	v.vid = vehicle.ID
-	v.bulkG = provider.Cached(v.bulk, cc.Cache)
+	v.bulkG = plugin.Cached(v.bulk, cc.Cache)
 
 	var status func() (api.ChargeStatus, error)
 	if slices.Contains(vehicle.Scopes, tronity.ReadCharge) {
diff --git a/vehicle/vehicle.go b/vehicle/vehicle.go
index 4342fb45..ff92e565 100644
--- a/vehicle/vehicle.go
+++ b/vehicle/vehicle.go
@@ -6,7 +6,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 )
 
@@ -26,24 +26,24 @@ func init() {
 func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) {
 	var cc struct {
 		embed         `mapstructure:",squash"`
-		Soc           provider.Config
-		LimitSoc      *provider.Config
-		Status        *provider.Config
-		Range         *provider.Config
-		Odometer      *provider.Config
-		Climater      *provider.Config
-		MaxCurrent    *provider.Config
-		GetMaxCurrent *provider.Config
-		FinishTime    *provider.Config
-		Wakeup        *provider.Config
-		ChargeEnable  *provider.Config
+		Soc           plugin.Config
+		LimitSoc      *plugin.Config
+		Status        *plugin.Config
+		Range         *plugin.Config
+		Odometer      *plugin.Config
+		Climater      *plugin.Config
+		MaxCurrent    *plugin.Config
+		GetMaxCurrent *plugin.Config
+		FinishTime    *plugin.Config
+		Wakeup        *plugin.Config
+		ChargeEnable  *plugin.Config
 	}
 
 	if err := util.DecodeOther(other, &cc); err != nil {
 		return nil, err
 	}
 
-	socG, err := provider.NewFloatGetterFromConfig(ctx, cc.Soc)
+	socG, err := cc.Soc.FloatGetter(ctx)
 	if err != nil {
 		return nil, fmt.Errorf("soc: %w", err)
 	}
@@ -54,18 +54,15 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 	}
 
 	// decorate range
-	var limitSoc func() (int64, error)
-	if cc.LimitSoc != nil {
-		limitSoc, err = provider.NewIntGetterFromConfig(ctx, *cc.LimitSoc)
-		if err != nil {
-			return nil, fmt.Errorf("limitSoc: %w", err)
-		}
+	limitSoc, err := cc.LimitSoc.IntGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("limitSoc: %w", err)
 	}
 
 	// decorate status
 	var status func() (api.ChargeStatus, error)
 	if cc.Status != nil {
-		get, err := provider.NewStringGetterFromConfig(ctx, *cc.Status)
+		get, err := cc.Status.StringGetter(ctx)
 		if err != nil {
 			return nil, fmt.Errorf("status: %w", err)
 		}
@@ -79,54 +76,39 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 	}
 
 	// decorate range
-	var rng func() (int64, error)
-	if cc.Range != nil {
-		rng, err = provider.NewIntGetterFromConfig(ctx, *cc.Range)
-		if err != nil {
-			return nil, fmt.Errorf("range: %w", err)
-		}
+	rng, err := cc.Range.IntGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("range: %w", err)
 	}
 
 	// decorate odometer
-	var odo func() (float64, error)
-	if cc.Odometer != nil {
-		odo, err = provider.NewFloatGetterFromConfig(ctx, *cc.Odometer)
-		if err != nil {
-			return nil, fmt.Errorf("odometer: %w", err)
-		}
+	odo, err := cc.Odometer.FloatGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("odometer: %w", err)
 	}
 
 	// decorate climater
-	var climater func() (bool, error)
-	if cc.Climater != nil {
-		climater, err = provider.NewBoolGetterFromConfig(ctx, *cc.Climater)
-		if err != nil {
-			return nil, fmt.Errorf("climater: %w", err)
-		}
+	climater, err := cc.Climater.BoolGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("climater: %w", err)
 	}
 
 	// decorate maxCurrent
-	var maxCurrent func(int64) error
-	if cc.MaxCurrent != nil {
-		maxCurrent, err = provider.NewIntSetterFromConfig(ctx, "maxcurrent", *cc.MaxCurrent)
-		if err != nil {
-			return nil, fmt.Errorf("maxCurrent: %w", err)
-		}
+	maxCurrent, err := cc.MaxCurrent.IntSetter(ctx, "maxcurrent")
+	if err != nil {
+		return nil, fmt.Errorf("maxCurrent: %w", err)
 	}
 
 	// decorate getMaxCurrent
-	var getMaxCurrent func() (float64, error)
-	if cc.GetMaxCurrent != nil {
-		getMaxCurrent, err = provider.NewFloatGetterFromConfig(ctx, *cc.GetMaxCurrent)
-		if err != nil {
-			return nil, fmt.Errorf("getMaxCurrent: %w", err)
-		}
+	getMaxCurrent, err := cc.GetMaxCurrent.FloatGetter(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("getMaxCurrent: %w", err)
 	}
 
 	// decorate finishtime
 	var finishTime func() (time.Time, error)
 	if cc.FinishTime != nil {
-		stringG, err := provider.NewStringGetterFromConfig(ctx, *cc.FinishTime)
+		stringG, err := cc.FinishTime.StringGetter(ctx)
 		if err != nil {
 			return nil, fmt.Errorf("finishTime: %w", err)
 		}
@@ -142,7 +124,7 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 	// decorate wakeup
 	var wakeup func() error
 	if cc.Wakeup != nil {
-		set, err := provider.NewBoolSetterFromConfig(ctx, "wakeup", *cc.Wakeup)
+		set, err := cc.Wakeup.BoolSetter(ctx, "wakeup")
 		if err != nil {
 			return nil, fmt.Errorf("wakeup: %w", err)
 		}
@@ -152,12 +134,9 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
 	}
 
 	// decorate chargeEnable
-	var chargeEnable func(bool) error
-	if cc.ChargeEnable != nil {
-		chargeEnable, err = provider.NewBoolSetterFromConfig(ctx, "chargeenable", *cc.ChargeEnable)
-		if err != nil {
-			return nil, fmt.Errorf("chargeEnable: %w", err)
-		}
+	chargeEnable, err := cc.ChargeEnable.BoolSetter(ctx, "chargeenable")
+	if err != nil {
+		return nil, fmt.Errorf("chargeEnable: %w", err)
 	}
 
 	return decorateVehicle(v, limitSoc, status, rng, odo, climater, maxCurrent, getMaxCurrent, finishTime, wakeup, chargeEnable), nil
diff --git a/vehicle/volvo.go b/vehicle/volvo.go
index fd4b9400..0711809c 100644
--- a/vehicle/volvo.go
+++ b/vehicle/volvo.go
@@ -5,7 +5,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
 	"github.com/evcc-io/evcc/util/transport"
@@ -64,7 +64,7 @@ func NewVolvoFromConfig(other map[string]interface{}) (api.Vehicle, error) {
 		}),
 	}
 
-	v.statusG = provider.Cached(v.StatusRequest, cc.Cache)
+	v.statusG = plugin.Cached(v.StatusRequest, cc.Cache)
 
 	var err error
 	v.vin, err = ensureVehicle(cc.VIN, v.Vehicles)
diff --git a/vehicle/volvo/connected/provider.go b/vehicle/volvo/connected/provider.go
index 2a2b9d7b..0e613372 100644
--- a/vehicle/volvo/connected/provider.go
+++ b/vehicle/volvo/connected/provider.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 // Provider implements the vehicle api
@@ -15,7 +15,7 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (RechargeStatus, error) {
+		statusG: plugin.Cached(func() (RechargeStatus, error) {
 			return api.RechargeStatus(vin)
 		}, cache),
 	}
diff --git a/vehicle/vw/id/provider.go b/vehicle/vw/id/provider.go
index cf0156fe..8335ada0 100644
--- a/vehicle/vw/id/provider.go
+++ b/vehicle/vw/id/provider.go
@@ -6,7 +6,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 // Provider is an api.Vehicle implementation for VW ID cars
@@ -18,7 +18,7 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		statusG: provider.Cached(func() (Status, error) {
+		statusG: plugin.Cached(func() (Status, error) {
 			return api.Status(vin)
 		}, cache),
 		action: func(action, value string) error {
diff --git a/vehicle/vw/provider.go b/vehicle/vw/provider.go
index 64faf061..c8f90f2a 100644
--- a/vehicle/vw/provider.go
+++ b/vehicle/vw/provider.go
@@ -11,7 +11,7 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 	"github.com/samber/lo"
 )
 
@@ -28,16 +28,16 @@ type Provider struct {
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, vin string, cache time.Duration) *Provider {
 	impl := &Provider{
-		chargerG: provider.Cached(func() (ChargerResponse, error) {
+		chargerG: plugin.Cached(func() (ChargerResponse, error) {
 			return api.Charger(vin)
 		}, cache),
-		statusG: provider.Cached(func() (StatusResponse, error) {
+		statusG: plugin.Cached(func() (StatusResponse, error) {
 			return api.Status(vin)
 		}, cache),
-		climateG: provider.Cached(func() (ClimaterResponse, error) {
+		climateG: plugin.Cached(func() (ClimaterResponse, error) {
 			return api.Climater(vin)
 		}, cache),
-		positionG: provider.Cached(func() (PositionResponse, error) {
+		positionG: plugin.Cached(func() (PositionResponse, error) {
 			return api.Position(vin)
 		}, cache),
 		action: func(action, value string) error {
diff --git a/vehicle/zero/provider.go b/vehicle/zero/provider.go
index 05fba1e2..2c96c2f1 100644
--- a/vehicle/zero/provider.go
+++ b/vehicle/zero/provider.go
@@ -4,18 +4,18 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/plugin"
 )
 
 // Provider implements the vehicle api
 type Provider struct {
-	status provider.Cacheable[State]
+	status plugin.Cacheable[State]
 }
 
 // NewProvider creates a vehicle api provider
 func NewProvider(api *API, unitId string, cache time.Duration) *Provider {
 	impl := &Provider{
-		status: provider.ResettableCached(func() (State, error) {
+		status: plugin.ResettableCached(func() (State, error) {
 			return api.Status(unitId)
 		}, cache),
 	}


Test patch:
diff --git a/provider/cache_test.go b/plugin/cache_test.go
similarity index 99%
rename from provider/cache_test.go
rename to plugin/cache_test.go
index b1c48473..8d4984f2 100644
--- a/provider/cache_test.go
+++ b/plugin/cache_test.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"errors"
diff --git a/plugin/config_test.go b/plugin/config_test.go
new file mode 100644
index 00000000..cef0ca16
--- /dev/null
+++ b/plugin/config_test.go
@@ -0,0 +1,20 @@
+package plugin
+
+import (
+	"context"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestConfig(t *testing.T) {
+	var c *Config
+
+	g, err := c.BoolGetter(context.TODO())
+	assert.NoError(t, err)
+	assert.Nil(t, g)
+
+	s, err := c.IntSetter(context.TODO(), "foo")
+	assert.NoError(t, err)
+	assert.Nil(t, s)
+}
diff --git a/provider/const_test.go b/plugin/const_test.go
similarity index 78%
rename from provider/const_test.go
rename to plugin/const_test.go
index aff1e4cf..193b45b9 100644
--- a/provider/const_test.go
+++ b/plugin/const_test.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -12,7 +12,7 @@ func TestConst(t *testing.T) {
 	assert.NoError(t, err)
 
 	{
-		g, err := p.(StringProvider).StringGetter()
+		g, err := p.(StringGetter).StringGetter()
 		assert.NoError(t, err)
 
 		v, err := g()
@@ -21,7 +21,7 @@ func TestConst(t *testing.T) {
 	}
 
 	{
-		g, err := p.(IntProvider).IntGetter()
+		g, err := p.(IntGetter).IntGetter()
 		assert.NoError(t, err)
 
 		v, err := g()
@@ -30,7 +30,7 @@ func TestConst(t *testing.T) {
 	}
 
 	{
-		g, err := p.(FloatProvider).FloatGetter()
+		g, err := p.(FloatGetter).FloatGetter()
 		assert.NoError(t, err)
 
 		v, err := g()
diff --git a/provider/http_test.go b/plugin/http_test.go
similarity index 99%
rename from provider/http_test.go
rename to plugin/http_test.go
index 417fe053..0c33409b 100644
--- a/provider/http_test.go
+++ b/plugin/http_test.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"net/http"
diff --git a/provider/socket_test.go b/plugin/socket_test.go
similarity index 88%
rename from provider/socket_test.go
rename to plugin/socket_test.go
index 757d72e4..6375485e 100644
--- a/provider/socket_test.go
+++ b/plugin/socket_test.go
@@ -1,4 +1,4 @@
-package provider
+package plugin
 
 import (
 	"context"
@@ -12,7 +12,7 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-func TestSocketProvider(t *testing.T) {
+func TestSockePlugin(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
 	defer cancel()
 
@@ -40,7 +40,7 @@ func TestSocketProvider(t *testing.T) {
 	defer srv.Close()
 
 	addr := "ws://" + srv.Listener.Addr().String()
-	p, err := NewSocketProviderFromConfig(map[string]any{
+	p, err := NewSocketPluginFromConfig(map[string]any{
 		"uri": addr,
 		"jq":  `.data | select(.uuid=="bar") .tuples[0][1]`,
 	})
@@ -48,7 +48,7 @@ func TestSocketProvider(t *testing.T) {
 
 	<-p.(*Socket).val.Done()
 
-	g, err := p.(IntProvider).IntGetter()
+	g, err := p.(IntGetter).IntGetter()
 	require.NoError(t, err)
 
 	i, err := g()