Plugins: allow chaining of input/output plugins (#7836)

This commit is contained in:
Markus Heberling 2023-05-18 14:08:05 +02:00 • committed by GitHub
parent 370b15aaca
commit cba97832db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 582 additions and 199 deletions

View file

@ -19,11 +19,7 @@ javascript:
{ enabled: false, vehicleSoc: 22, maxcurrent: 0, phases: 3, chargepower: 0 }
]
};
function get() {
console.log("state:", JSON.stringify(state));
}
function set() {
console.log(param+":", val);
function logState() {
console.log("state:", JSON.stringify(state));
}
@ -34,10 +30,25 @@ meters:
source: js
vm: shared
script: |
state.pvpower = 8000+500*Math.random();
state.batterypower = state.gridpower > 0 ? 1000 * Math.random() : 0;
state.gridpower = state.loadpoints[0].chargepower + state.loadpoints[1].chargepower + state.residualpower - state.batterypower - state.pvpower;
state.gridpower = state.loadpoints[0].chargepower + state.loadpoints[1].chargepower + state.residualpower - batterypower - pvpower;
state.gridpower;
in:
- name: pvpower
type: float
config:
source: js
vm: shared
script: |
state.pvpower = 8000+500*Math.random();
state.pvpower
- name: batterypower
type: float
config:
source: js
vm: shared
script: |
state.batterypower = state.gridpower > 0 ? 1000 * Math.random() : 0;
state.batterypower
- name: pv
type: custom
@ -83,10 +94,18 @@ chargers:
source: js
vm: shared
script: |
set();
logState();
var lp = state.loadpoints[0];
lp.enabled = val;
if (lp.enabled) lp.chargepower = lp.maxcurrent * 230 * lp.phases; else lp.chargepower = 0;
lp.enabled = enable;
enable;
out:
- name: enable
type: bool
config:
source: js
vm: shared
script: |
if (enable) lp.chargepower = lp.maxcurrent * 230 * lp.phases; else lp.chargepower = 0;
enabled:
source: js
vm: shared
@ -101,9 +120,9 @@ chargers:
source: js
vm: shared
script: |
set();
logState();
var lp = state.loadpoints[0];
lp.maxcurrent = val;
lp.maxcurrent = maxcurrent;
if (lp.enabled) lp.chargepower = lp.maxcurrent * 230 * lp.phases; else lp.chargepower = 0;
- name: charger_2
@ -112,9 +131,9 @@ chargers:
source: js
vm: shared
script: |
set();
logState();
var lp = state.loadpoints[1];
lp.enabled = val;
lp.enabled = enable;
if (lp.enabled) lp.chargepower = lp.maxcurrent * 230 * lp.phases; else lp.chargepower = 0;
enabled:
source: js
@ -130,16 +149,16 @@ chargers:
source: js
vm: shared
script: |
set();
logState();
var lp = state.loadpoints[1];
lp.maxcurrent = val;
lp.maxcurrent = maxcurrent;
if (lp.enabled) lp.chargepower = lp.maxcurrent * 230 * lp.phases; else lp.chargepower = 0;
phases1p3p:
source: js
vm: shared
script: |
set();
if (val === 1) lp.phases = 1; else lp.phases = 3;
logState();
if (phases === 1) lp.phases = 1; else lp.phases = 3;
lp.phases;
vehicles:

View file

@ -18,7 +18,7 @@ func init() {
}
// NewCalcFromConfig creates calc provider
func NewCalcFromConfig(other map[string]interface{}) (IntProvider, error) {
func NewCalcFromConfig(other map[string]interface{}) (Provider, error) {
var cc struct {
Add []Config
Mul []Config

27
provider/combined.go Normal file
View file

@ -0,0 +1,27 @@
package provider
type combinedProvider struct {
status func() (string, error)
}
func init() {
registry.Add("combined", NewCombinedFromConfig)
registry.Add("openwb", NewCombinedFromConfig)
}
// NewCombinedFromConfig creates combined provider
func NewCombinedFromConfig(other map[string]interface{}) (Provider, error) {
status, err := NewOpenWBStatusProviderFromConfig(other)
if err != nil {
return nil, err
}
o := &combinedProvider{status: status}
return o, nil
}
func (o *combinedProvider) StringGetter() func() (string, error) {
return func() (string, error) {
return o.status()
}
}

View file

@ -6,6 +6,7 @@ import (
// provider types
type (
Provider interface{}
IntProvider interface {
IntGetter() func() (int64, error)
}
@ -24,21 +25,24 @@ type (
SetStringProvider interface {
StringSetter(param string) func(string) error
}
SetFloatProvider interface {
FloatSetter(param string) func(float64) error
}
SetBoolProvider interface {
BoolSetter(param string) func(bool) error
}
)
type providerRegistry map[string]func(map[string]interface{}) (IntProvider, error)
type providerRegistry map[string]func(map[string]interface{}) (Provider, error)
func (r providerRegistry) Add(name string, factory func(map[string]interface{}) (IntProvider, error)) {
func (r providerRegistry) Add(name string, factory func(map[string]interface{}) (Provider, error)) {
if _, exists := r[name]; exists {
panic(fmt.Sprintf("cannot register duplicate plugin type: %s", name))
}
r[name] = factory
}
func (r providerRegistry) Get(name string) (func(map[string]interface{}) (IntProvider, error), error) {
func (r providerRegistry) Get(name string) (func(map[string]interface{}) (Provider, error), error) {
factory, exists := r[name]
if !exists {
return nil, fmt.Errorf("invalid plugin source: %s", name)
@ -46,7 +50,7 @@ func (r providerRegistry) Get(name string) (func(map[string]interface{}) (IntPro
return factory, nil
}
var registry providerRegistry = make(map[string]func(map[string]interface{}) (IntProvider, error))
var registry providerRegistry = make(map[string]func(map[string]interface{}) (Provider, error))
// Config is the general provider config
type Config struct {
@ -55,122 +59,161 @@ type Config struct {
}
// NewIntGetterFromConfig creates a IntGetter from config
func NewIntGetterFromConfig(config Config) (res func() (int64, error), err error) {
func NewIntGetterFromConfig(config Config) (func() (int64, error), error) {
factory, err := registry.Get(config.Source)
if err == nil {
var provider IntProvider
provider, err = factory(config.Other)
if err == nil {
res = provider.IntGetter()
}
if err != nil {
return nil, err
}
if err == nil && res == nil {
err = fmt.Errorf("invalid plugin source: %s", config.Source)
provider, err := factory(config.Other)
if err != nil {
return nil, err
}
return
prov, ok := provider.(IntProvider)
if !ok {
return nil, fmt.Errorf("invalid plugin source for type int: %s", config.Source)
}
return prov.IntGetter(), nil
}
// NewFloatGetterFromConfig creates a FloatGetter from config
func NewFloatGetterFromConfig(config Config) (res func() (float64, error), err error) {
func NewFloatGetterFromConfig(config Config) (func() (float64, error), error) {
factory, err := registry.Get(config.Source)
if err == nil {
var provider IntProvider
provider, err = factory(config.Other)
if prov, ok := provider.(FloatProvider); ok {
res = prov.FloatGetter()
}
if err != nil {
return nil, err
}
if err == nil && res == nil {
err = fmt.Errorf("invalid plugin source: %s", config.Source)
provider, err := factory(config.Other)
if err != nil {
return nil, err
}
return
prov, ok := provider.(FloatProvider)
if !ok {
return nil, fmt.Errorf("invalid plugin source for type float: %s", config.Source)
}
return prov.FloatGetter(), nil
}
// NewStringGetterFromConfig creates a StringGetter from config
func NewStringGetterFromConfig(config Config) (res func() (string, error), err error) {
switch typ := config.Source; typ {
case "combined", "openwb":
res, err = NewOpenWBStatusProviderFromConfig(config.Other)
default:
var factory func(map[string]interface{}) (IntProvider, error)
factory, err = registry.Get(typ)
if err == nil {
var provider IntProvider
provider, err = factory(config.Other)
if prov, ok := provider.(StringProvider); ok {
res = prov.StringGetter()
}
}
if err == nil && res == nil {
err = fmt.Errorf("invalid plugin source: %s", config.Source)
}
func NewStringGetterFromConfig(config Config) (func() (string, error), error) {
factory, err := registry.Get(config.Source)
if err != nil {
return nil, err
}
return
provider, err := factory(config.Other)
if err != nil {
return nil, err
}
prov, ok := provider.(StringProvider)
if !ok {
return nil, fmt.Errorf("invalid plugin source for type string: %s", config.Source)
}
return prov.StringGetter(), nil
}
// NewBoolGetterFromConfig creates a BoolGetter from config
func NewBoolGetterFromConfig(config Config) (res func() (bool, error), err error) {
func NewBoolGetterFromConfig(config Config) (func() (bool, error), error) {
factory, err := registry.Get(config.Source)
if err == nil {
var provider IntProvider
provider, err = factory(config.Other)
if prov, ok := provider.(BoolProvider); ok {
res = prov.BoolGetter()
}
if err != nil {
return nil, err
}
if err == nil && res == nil {
err = fmt.Errorf("invalid plugin source: %s", config.Source)
provider, err := factory(config.Other)
if err != nil {
return nil, err
}
return
prov, ok := provider.(BoolProvider)
if !ok {
return nil, fmt.Errorf("invalid plugin source for type bool: %s", config.Source)
}
return prov.BoolGetter(), nil
}
// NewIntSetterFromConfig creates a IntSetter from config
func NewIntSetterFromConfig(param string, config Config) (res func(int64) error, err error) {
func NewIntSetterFromConfig(param string, config Config) (func(int64) error, error) {
factory, err := registry.Get(config.Source)
if err == nil {
var provider IntProvider
provider, err = factory(config.Other)
if prov, ok := provider.(SetIntProvider); ok {
res = prov.IntSetter(param)
}
if err != nil {
return nil, err
}
if err == nil && res == nil {
err = fmt.Errorf("invalid plugin source: %s", config.Source)
provider, err := factory(config.Other)
if err != nil {
return nil, err
}
return
prov, ok := provider.(SetIntProvider)
if !ok {
return nil, fmt.Errorf("invalid plugin source for type int: %s", config.Source)
}
return prov.IntSetter(param), nil
}
// NewFloatSetterFromConfig creates a FloatSetter from config
func NewFloatSetterFromConfig(param string, config Config) (func(float642 float64) error, error) {
factory, err := registry.Get(config.Source)
if err != nil {
return nil, err
}
provider, err := factory(config.Other)
if err != nil {
return nil, err
}
prov, ok := provider.(SetFloatProvider)
if !ok {
return nil, fmt.Errorf("invalid plugin source for type float: %s", config.Source)
}
return prov.FloatSetter(param), nil
}
// NewStringSetterFromConfig creates a StringSetter from config
func NewStringSetterFromConfig(param string, config Config) (func(string) error, error) {
factory, err := registry.Get(config.Source)
if err != nil {
return nil, err
}
provider, err := factory(config.Other)
if err != nil {
return nil, err
}
prov, ok := provider.(SetStringProvider)
if !ok {
return nil, fmt.Errorf("invalid plugin source for type string: %s", config.Source)
}
return prov.StringSetter(param), nil
}
// NewBoolSetterFromConfig creates a BoolSetter from config
func NewBoolSetterFromConfig(param string, config Config) (res func(bool) error, err error) {
func NewBoolSetterFromConfig(param string, config Config) (func(bool) error, error) {
factory, err := registry.Get(config.Source)
if err == nil {
var provider IntProvider
provider, err = factory(config.Other)
if prov, ok := provider.(SetBoolProvider); ok {
res = prov.BoolSetter(param)
}
if err != nil {
return nil, err
}
if err == nil && res == nil {
err = fmt.Errorf("invalid plugin source: %s", config.Source)
provider, err := factory(config.Other)
if err != nil {
return nil, err
}
return
prov, ok := provider.(SetBoolProvider)
if !ok {
return nil, fmt.Errorf("invalid plugin source for type bool: %s", config.Source)
}
return prov.BoolSetter(param), nil
}

View file

@ -16,7 +16,7 @@ func init() {
}
// NewConstFromConfig creates const provider
func NewConstFromConfig(other map[string]interface{}) (IntProvider, error) {
func NewConstFromConfig(other map[string]interface{}) (Provider, error) {
var cc struct {
Value string
pipeline.Settings `mapstructure:",squash"`

View file

@ -2,7 +2,6 @@ package provider
import (
"fmt"
"reflect"
"github.com/evcc-io/evcc/provider/golang"
"github.com/evcc-io/evcc/util"
@ -13,6 +12,8 @@ import (
type Go struct {
vm *interp.Interpreter
script string
in []inputTransformation
out []outputTransformation
}
func init() {
@ -20,10 +21,12 @@ func init() {
}
// NewGoProviderFromConfig creates a Go provider
func NewGoProviderFromConfig(other map[string]interface{}) (IntProvider, error) {
func NewGoProviderFromConfig(other map[string]interface{}) (Provider, error) {
var cc struct {
VM string
Script string
In []transformationConfig
Out []transformationConfig
}
if err := util.DecodeOther(other, &cc); err != nil {
@ -35,9 +38,21 @@ func NewGoProviderFromConfig(other map[string]interface{}) (IntProvider, error)
return nil, err
}
in, err := configureInputs(cc.In)
if err != nil {
return nil, err
}
out, err := configureOutputs(cc.Out)
if err != nil {
return nil, err
}
p := &Go{
vm: vm,
script: cc.Script,
in: in,
out: out,
}
return p, nil
@ -45,92 +60,135 @@ func NewGoProviderFromConfig(other map[string]interface{}) (IntProvider, error)
// FloatGetter parses float from request
func (p *Go) FloatGetter() func() (float64, error) {
return func() (res float64, err error) {
v, err := p.vm.Eval(p.script)
if err == nil {
if typ := reflect.TypeOf(res); v.CanConvert(typ) {
res = v.Convert(typ).Float()
} else {
err = fmt.Errorf("not a float: %v", v)
}
return func() (float64, error) {
v, err := p.handleGetter()
if err != nil {
return 0, err
}
return res, err
vv, ok := v.(float64)
if !ok {
return 0, fmt.Errorf("not a float: %s", v)
}
return vv, nil
}
}
// IntGetter parses int64 from request
func (p *Go) IntGetter() func() (int64, error) {
return func() (res int64, err error) {
v, err := p.vm.Eval(p.script)
if err == nil {
if typ := reflect.TypeOf(res); v.CanConvert(typ) {
res = v.Convert(typ).Int()
} else {
err = fmt.Errorf("not an int: %v", v)
}
return func() (int64, error) {
v, err := p.handleGetter()
if err != nil {
return 0, err
}
return res, err
vv, ok := v.(int64)
if !ok {
return 0, fmt.Errorf("not a int: %s", v)
}
return vv, nil
}
}
// StringGetter parses string from request
func (p *Go) StringGetter() func() (string, error) {
return func() (res string, err error) {
v, err := p.vm.Eval(p.script)
if err == nil {
if typ := reflect.TypeOf(res); v.CanConvert(typ) {
res = v.Convert(typ).String()
} else {
err = fmt.Errorf("not a string: %v", v)
}
return func() (string, error) {
v, err := p.handleGetter()
if err != nil {
return "", err
}
return res, err
vv, ok := v.(string)
if !ok {
return "", fmt.Errorf("not a string: %s", v)
}
return vv, nil
}
}
// BoolGetter parses bool from request
func (p *Go) BoolGetter() func() (bool, error) {
return func() (res bool, err error) {
v, err := p.vm.Eval(p.script)
if err == nil {
if typ := reflect.TypeOf(res); v.CanConvert(typ) {
res = v.Convert(typ).Bool()
} else {
err = fmt.Errorf("not a boolean: %v", v)
}
return func() (bool, error) {
v, err := p.handleGetter()
if err != nil {
return false, err
}
return res, err
vv, ok := v.(bool)
if !ok {
return false, fmt.Errorf("not a bool: %s", v)
}
return vv, nil
}
}
func (p *Go) paramAndEval(param string, val any) error {
_, err := p.vm.Eval(fmt.Sprintf("%s := %v;", param, val))
if err == nil {
_, err = p.vm.Eval(p.script)
func (p *Go) handleGetter() (any, error) {
if err := transformInputs(p.in, p.setParam); err != nil {
return nil, err
}
return p.evaluate()
}
func (p *Go) handleSetter(param string, val any) error {
if err := p.setParam(param, val); err != nil {
return err
}
vv, err := p.evaluate()
if err != nil {
return err
}
return transformOutputs(p.out, vv)
}
func (p *Go) evaluate() (any, error) {
v, err := p.vm.Eval(p.script)
if err != nil {
return nil, err
}
return normalizeValue(v.Interface())
}
func (p *Go) setParam(param string, val any) error {
if str, ok := val.(string); ok {
val = "\"" + str + "\""
}
_, err := p.vm.Eval(fmt.Sprintf("%s := %v;", param, val))
return err
}
// IntSetter sends int request
func (p *Go) IntSetter(param string) func(int64) error {
return func(val int64) error {
return p.paramAndEval(param, val)
return p.handleSetter(param, val)
}
}
// FloatSetter sends float request
func (p *Go) FloatSetter(param string) func(float64) error {
return func(val float64) error {
return p.handleSetter(param, val)
}
}
// StringSetter sends string request
func (p *Go) StringSetter(param string) func(string) error {
return func(val string) error {
return p.paramAndEval(param, val)
return p.handleSetter(param, val)
}
}
// BoolSetter sends bool request
func (p *Go) BoolSetter(param string) func(bool) error {
return func(val bool) error {
return p.paramAndEval(param, val)
return p.handleSetter(param, val)
}
}

View file

@ -39,7 +39,7 @@ type Auth struct {
}
// NewHTTPProviderFromConfig creates a HTTP provider
func NewHTTPProviderFromConfig(other map[string]interface{}) (IntProvider, error) {
func NewHTTPProviderFromConfig(other map[string]interface{}) (Provider, error) {
cc := struct {
URI, Method string
Headers map[string]string

View file

@ -1,6 +1,8 @@
package provider
import (
"fmt"
"github.com/evcc-io/evcc/provider/javascript"
"github.com/evcc-io/evcc/util"
"github.com/robertkrimen/otto"
@ -10,17 +12,21 @@ import (
type Javascript struct {
vm *otto.Otto
script string
in []inputTransformation
out []outputTransformation
}
func init() {
registry.Add("js", NewJavascriptProviderFromConfig)
}
// NewJavascriptProviderFromConfig creates a HTTP provider
func NewJavascriptProviderFromConfig(other map[string]interface{}) (IntProvider, error) {
// NewJavascriptProviderFromConfig creates a Javascript provider
func NewJavascriptProviderFromConfig(other map[string]interface{}) (Provider, error) {
var cc struct {
VM string
Script string
In []transformationConfig
Out []transformationConfig
}
if err := util.DecodeOther(other, &cc); err != nil {
@ -32,9 +38,21 @@ func NewJavascriptProviderFromConfig(other map[string]interface{}) (IntProvider,
return nil, err
}
in, err := configureInputs(cc.In)
if err != nil {
return nil, err
}
out, err := configureOutputs(cc.Out)
if err != nil {
return nil, err
}
p := &Javascript{
vm: vm,
script: cc.Script,
in: in,
out: out,
}
return p, nil
@ -42,92 +60,140 @@ func NewJavascriptProviderFromConfig(other map[string]interface{}) (IntProvider,
// FloatGetter parses float from request
func (p *Javascript) FloatGetter() func() (float64, error) {
return func() (res float64, err error) {
v, err := p.vm.Eval(p.script)
if err == nil {
res, err = v.ToFloat()
return func() (float64, error) {
v, err := p.handleGetter()
if err != nil {
return 0, err
}
return res, err
vv, ok := v.(float64)
if !ok {
return 0, fmt.Errorf("not a float: %s", v)
}
return vv, nil
}
}
// IntGetter parses int64 from request
func (p *Javascript) IntGetter() func() (int64, error) {
return func() (res int64, err error) {
v, err := p.vm.Eval(p.script)
if err == nil {
res, err = v.ToInteger()
return func() (int64, error) {
v, err := p.handleGetter()
if err != nil {
return 0, err
}
return res, err
vv, ok := v.(int64)
if !ok {
return 0, fmt.Errorf("not a int: %s", v)
}
return vv, nil
}
}
// StringGetter sends string request
// StringGetter parses string from request
func (p *Javascript) StringGetter() func() (string, error) {
return func() (res string, err error) {
v, err := p.vm.Eval(p.script)
if err == nil {
res, err = v.ToString()
return func() (string, error) {
v, err := p.handleGetter()
if err != nil {
return "", err
}
return res, err
vv, ok := v.(string)
if !ok {
return "", fmt.Errorf("not a string: %s", v)
}
return vv, nil
}
}
// BoolGetter parses bool from request
func (p *Javascript) BoolGetter() func() (bool, error) {
return func() (res bool, err error) {
v, err := p.vm.Eval(p.script)
if err == nil {
res, err = v.ToBoolean()
return func() (bool, error) {
v, err := p.handleGetter()
if err != nil {
return false, err
}
return res, err
vv, ok := v.(bool)
if !ok {
return false, fmt.Errorf("not a bool: %s", v)
}
return vv, nil
}
}
func (p *Javascript) setParam(param string, val interface{}) error {
err := p.vm.Set(param, val)
if err == nil {
err = p.vm.Set("param", param)
func (p *Javascript) handleGetter() (any, error) {
if err := transformInputs(p.in, p.setParam); err != nil {
return nil, err
}
if err == nil {
err = p.vm.Set("val", val)
v, err := p.evaluate()
if err != nil {
return nil, err
}
return err
return v, nil
}
func (p *Javascript) handleSetter(param string, val any) error {
if err := p.setParam(param, val); err != nil {
return err
}
v, err := p.evaluate()
if err != nil {
return err
}
return transformOutputs(p.out, v)
}
func (p *Javascript) evaluate() (any, error) {
v, err := p.vm.Eval(p.script)
if err != nil {
return nil, err
}
vv, err := v.Export()
if err != nil {
return nil, err
}
return normalizeValue(vv)
}
func (p *Javascript) setParam(param string, val any) error {
return p.vm.Set(param, val)
}
// IntSetter sends int request
func (p *Javascript) IntSetter(param string) func(int64) error {
return func(val int64) error {
err := p.setParam(param, val)
if err == nil {
_, err = p.vm.Eval(p.script)
}
return err
return p.handleSetter(param, val)
}
}
// FloatSetter sends float request
func (p *Javascript) FloatSetter(param string) func(float64) error {
return func(val float64) error {
return p.handleSetter(param, val)
}
}
// StringSetter sends string request
func (p *Javascript) StringSetter(param string) func(string) error {
return func(val string) error {
err := p.setParam(param, val)
if err == nil {
_, err = p.vm.Eval(p.script)
}
return err
return p.handleSetter(param, val)
}
}
// BoolSetter sends bool request
func (p *Javascript) BoolSetter(param string) func(bool) error {
return func(val bool) error {
err := p.setParam(param, val)
if err == nil {
_, err = p.vm.Eval(p.script)
}
return err
return p.handleSetter(param, val)
}
}

View file

@ -31,7 +31,7 @@ func init() {
}
// NewModbusFromConfig creates Modbus plugin
func NewModbusFromConfig(other map[string]interface{}) (IntProvider, error) {
func NewModbusFromConfig(other map[string]interface{}) (Provider, error) {
cc := struct {
Model string
modbus.Settings `mapstructure:",squash"`

View file

@ -25,7 +25,7 @@ func init() {
}
// NewMqttFromConfig creates Mqtt provider
func NewMqttFromConfig(other map[string]interface{}) (IntProvider, error) {
func NewMqttFromConfig(other map[string]interface{}) (Provider, error) {
cc := struct {
mqtt.Config `mapstructure:",squash"`
Topic, Payload string // Payload only applies to setters

View file

@ -26,7 +26,7 @@ func init() {
registry.Add("prometheus", NewPrometheusFromConfig)
}
func NewPrometheusFromConfig(other map[string]interface{}) (IntProvider, error) {
func NewPrometheusFromConfig(other map[string]interface{}) (Provider, error) {
cc := struct {
Uri, Query string
Timeout time.Duration

View file

@ -37,7 +37,7 @@ func init() {
}
// NewScriptProviderFromConfig creates a script provider.
func NewScriptProviderFromConfig(other map[string]interface{}) (IntProvider, error) {
func NewScriptProviderFromConfig(other map[string]interface{}) (Provider, error) {
cc := struct {
Cmd string
Timeout time.Duration

View file

@ -21,7 +21,7 @@ func init() {
}
// NewSMAFromConfig creates SMA provider
func NewSMAFromConfig(other map[string]interface{}) (IntProvider, error) {
func NewSMAFromConfig(other map[string]interface{}) (Provider, error) {
cc := struct {
URI, Password, Interface string
Serial uint32

View file

@ -37,7 +37,7 @@ func init() {
}
// NewSocketProviderFromConfig creates a HTTP provider
func NewSocketProviderFromConfig(other map[string]interface{}) (IntProvider, error) {
func NewSocketProviderFromConfig(other map[string]interface{}) (Provider, error) {
cc := struct {
URI string
Headers map[string]string

View file

@ -46,7 +46,7 @@ func TestSocketProvider(t *testing.T) {
})
require.NoError(t, err)
g := p.IntGetter()
g := p.(IntProvider).IntGetter()
i, err := g()
require.NoError(t, err)
require.Equal(t, int64(1), i)

170
provider/transformation.go Normal file
View file

@ -0,0 +1,170 @@
package provider
import (
"fmt"
"strings"
)
type transformationConfig struct {
Name, Type string
Config Config
}
type inputTransformation struct {
name string
function func() (any, error)
}
type outputTransformation struct {
name string
function func(any) error
}
func configureInputs(inConfig []transformationConfig) ([]inputTransformation, error) {
var in []inputTransformation
for _, cc := range inConfig {
var f func() (any, error)
switch strings.ToLower(cc.Type) {
case "bool":
ff, err := NewBoolGetterFromConfig(cc.Config)
if err != nil {
return nil, fmt.Errorf("%s: %w", cc.Name, err)
}
f = func() (any, error) { return ff() }
case "int":
ff, err := NewIntGetterFromConfig(cc.Config)
if err != nil {
return nil, fmt.Errorf("%s: %w", cc.Name, err)
}
f = func() (any, error) { return ff() }
case "float":
ff, err := NewFloatGetterFromConfig(cc.Config)
if err != nil {
return nil, fmt.Errorf("%s: %w", cc.Name, err)
}
f = func() (any, error) { return ff() }
case "string":
ff, err := NewStringGetterFromConfig(cc.Config)
if err != nil {
return nil, fmt.Errorf("%s: %w", cc.Name, err)
}
f = func() (any, error) { return ff() }
default:
return nil, fmt.Errorf("%s: Could not find converter for %s", cc.Name, cc.Type)
}
in = append(in, inputTransformation{
name: cc.Name,
function: f,
})
}
return in, nil
}
func configureOutputs(outConfig []transformationConfig) ([]outputTransformation, error) {
var out []outputTransformation
for _, cc := range outConfig {
var f func(v any) error
switch strings.ToLower(cc.Type) {
case "bool":
ff, err := NewBoolSetterFromConfig(cc.Name, cc.Config)
if err != nil {
return nil, fmt.Errorf("%s: %w", cc.Name, err)
}
f = func(v any) error {
return ff(v.(bool))
}
case "int":
ff, err := NewIntSetterFromConfig(cc.Name, cc.Config)
if err != nil {
return nil, fmt.Errorf("%s: %w", cc.Name, err)
}
f = func(v any) error {
return ff(v.(int64))
}
case "float":
ff, err := NewFloatSetterFromConfig(cc.Name, cc.Config)
if err != nil {
return nil, fmt.Errorf("%s: %w", cc.Name, err)
}
f = func(v any) error {
return ff(v.(float64))
}
case "string":
ff, err := NewStringSetterFromConfig(cc.Name, cc.Config)
if err != nil {
return nil, fmt.Errorf("%s: %w", cc.Name, err)
}
f = func(v any) error {
return ff(v.(string))
}
default:
return nil, fmt.Errorf("%s: invalid type %s", cc.Name, cc.Type)
}
out = append(out, outputTransformation{
name: cc.Name,
function: f,
})
}
return out, nil
}
func transformInputs(in []inputTransformation, set func(string, any) error) error {
for _, cc := range in {
val, err := cc.function()
if err == nil {
err = set(cc.name, val)
}
if err != nil {
return fmt.Errorf("%s: %w", cc.name, err)
}
}
return nil
}
func transformOutputs(out []outputTransformation, v any) error {
for _, cc := range out {
if err := cc.function(v); err != nil {
return fmt.Errorf("%s: %w", cc.name, err)
}
}
return nil
}
// normalizeValue transforms compatible plugin return types to ensure only supported ones are used
func normalizeValue(val any) (any, error) {
switch v := val.(type) {
case int:
return int64(v), nil
case int32:
return int64(v), nil
case float32:
return float64(v), nil
case int64, float64, bool, string:
return v, nil
default:
return nil, fmt.Errorf("type not supported: %T", val)
}
}