Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go plugin #6672

Merged
merged 6 commits into from Apr 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions cmd/config.go
Expand Up @@ -62,6 +62,7 @@ type config struct {
Mqtt mqttConfig
ModbusProxy []proxyConfig
Javascript []javascriptConfig
Go []goConfig
Influx server.InfluxConfig
EEBus map[string]interface{}
HEMS typedConfig
Expand All @@ -84,6 +85,11 @@ type javascriptConfig struct {
Script string
}

type goConfig struct {
VM string
Script string
}

type proxyConfig struct {
Port int
ReadOnly bool
Expand Down
16 changes: 16 additions & 0 deletions cmd/setup.go
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"errors"
"fmt"
"github.com/evcc-io/evcc/provider/golang"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -96,6 +97,11 @@ func configureEnvironment(cmd *cobra.Command, conf config) (err error) {
err = configureJavascript(conf.Javascript)
}

// setup go VMs
if err == nil {
err = configureGo(conf.Go)
}

// setup EEBus server
if err == nil && conf.EEBus != nil {
err = configureEEBus(conf.EEBus)
Expand Down Expand Up @@ -163,6 +169,16 @@ func configureJavascript(conf []javascriptConfig) error {
return nil
}

// setup go
func configureGo(conf []goConfig) error {
for _, cc := range conf {
if _, err := golang.RegisteredVM(cc.VM, cc.Script); err != nil {
return fmt.Errorf("failed configuring go: %w", err)
}
}
return nil
}

// setup HEMS
func configureHEMS(conf typedConfig, site *core.Site, httpd *server.HTTPd) error {
hems, err := hems.NewFromConfig(conf.Type, conf.Other, site, httpd)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Expand Up @@ -87,6 +87,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.2
github.com/traefik/yaegi v0.14.3
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c
github.com/volkszaehler/mbmd v0.0.0-20230312113724-f6764040a78e
github.com/writeas/go-strip-markdown v2.0.1+incompatible
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Expand Up @@ -1045,6 +1045,8 @@ github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cb
github.com/teivah/onecontext v1.3.0 h1:tbikMhAlo6VhAuEGCvhc8HlTnpX4xTNPTOseWuhO1J0=
github.com/teivah/onecontext v1.3.0/go.mod h1:hoW1nmdPVK/0jrvGtcx8sCKYs2PiS4z0zzfdeuEVyb0=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/traefik/yaegi v0.14.3 h1:LqA0k8DKwvRMc+msfQjNusphHJc+r6WC5tZU5TmUFOM=
github.com/traefik/yaegi v0.14.3/go.mod h1:AVRxhaI2G+nUsaM1zyktzwXn69G3t/AuTDrCiTds9p0=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
Expand Down
136 changes: 136 additions & 0 deletions provider/go.go
@@ -0,0 +1,136 @@
package provider

import (
"fmt"
"reflect"

"github.com/evcc-io/evcc/provider/golang"
"github.com/evcc-io/evcc/util"
"github.com/traefik/yaegi/interp"
)

// Go implements Go request provider
type Go struct {
vm *interp.Interpreter
script string
}

func init() {
registry.Add("go", NewGoProviderFromConfig)
}

// NewGoProviderFromConfig creates a Go provider
func NewGoProviderFromConfig(other map[string]interface{}) (IntProvider, error) {
var cc struct {
VM string
Script string
}

if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}

vm, err := golang.RegisteredVM(cc.VM, "")
if err != nil {
return nil, err
}

p := &Go{
vm: vm,
script: cc.Script,
}

return p, nil
}

// 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 res, err
}
}

// 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 res, err
}
}

// 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 res, err
}
}

// 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 res, err
}
}

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)
}
return err
}

func (p *Go) IntSetter(param string) func(int64) error {
return func(val int64) error {
return p.paramAndEval(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)
}
}

// BoolSetter sends bool request
func (p *Go) BoolSetter(param string) func(bool) error {
return func(val bool) error {
return p.paramAndEval(param, val)
}
}
43 changes: 43 additions & 0 deletions provider/golang/registry.go
@@ -0,0 +1,43 @@
package golang

import (
"github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib"
"strings"
"sync"
)

var (
mu sync.Mutex
registry = make(map[string]*interp.Interpreter)
)

// RegisteredVM returns a JS VM. If name is not empty, it will return a shared instance.
func RegisteredVM(name, init string) (*interp.Interpreter, error) {
mu.Lock()
defer mu.Unlock()

name = strings.ToLower(name)
vm, ok := registry[name]

// create new VM
if !ok {
vm = interp.New(interp.Options{})

if err := vm.Use(stdlib.Symbols); err != nil {
return nil, err
}

if init != "" {
if _, err := vm.Eval(init); err != nil {
return nil, err
}
}

if name != "" {
registry[name] = vm
}
}

return vm, nil
}