diff --git a/README.md b/README.md index 8caf49d81..f69634fd4 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ hems: to the configuration. The EVCC loadpoints can then be added to the SHM configuration. When SHM is used, the ratio of Grid to PV Power for the **Min+PV** mode can be adjusted in Sunny-Portal via the "Optional energy demand" slider. When the amount of configured PV is not available, charging suspends like in **PV** mode. So, pushing the slider completely -to the left makes **Min+PV** behave as described above. Pushing completely to the right makes **Min+PV** mode behave like **PV** mode. +to the left makes **Min+PV** behave as described above. Pushing completely to the right makes **Min+PV** mode behave like **PV** mode. ## Plugins @@ -265,9 +265,20 @@ add: The `calc` plugin is useful e.g. to combine power values if import and export power are separate like with S0 meters. Use `scale` on one of the elements to implement a subtraction. +### Javascript (read only) + +The `js` plugin is able to execute Javascript code from the `script` tag. Useful for quick prototyping: + +```yaml +type: js +script: | + var res = 500; + 2 * res; // returns 1000 +``` + ### Modbus (read only) -The `modbus` plugins is able to read data from any Modbus meter or SunSpec-compatible solar inverter. Many meters are already pre-configured (see [MBMD Supported Devices](https://github.com/volkszaehler/mbmd#supported-devices)). +The `modbus` plugin is able to read data from any Modbus meter or SunSpec-compatible solar inverter. Many meters are already pre-configured (see [MBMD Supported Devices](https://github.com/volkszaehler/mbmd#supported-devices)). The meter configuration consists of the actual physical connection and the value to be read. diff --git a/go.mod b/go.mod index 16287c561..dd3965805 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/mxschmitt/golang-combinations v1.1.0 github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20170819232839-0fbfe93532da github.com/olekukonko/tablewriter v0.0.4 + github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac github.com/spf13/cobra v1.1.1 github.com/spf13/jwalterweatherman v1.1.0 github.com/spf13/pflag v1.0.5 @@ -53,6 +54,7 @@ require ( github.com/volkszaehler/mbmd v0.0.0-20201205173745-5106cb0b334e golang.org/x/net v0.0.0-20200904194848-62affa334b73 gopkg.in/ini.v1 v1.62.0 + gopkg.in/sourcemap.v1 v1.0.5 // indirect gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 ) diff --git a/go.sum b/go.sum index 00c7201dd..5c77e93b8 100644 --- a/go.sum +++ b/go.sum @@ -244,6 +244,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac h1:kYPjbEN6YPYWWHI6ky1J813KzIq/8+Wg4TO4xU7A/KU= +github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= @@ -412,6 +414,8 @@ gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= +gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/provider/config.go b/provider/config.go index 7329d9cd0..d7fbc5739 100644 --- a/provider/config.go +++ b/provider/config.go @@ -72,6 +72,11 @@ func NewFloatGetterFromConfig(config Config) (res func() (float64, error), err e if prov, err = NewHTTPProviderFromConfig(config.Other); err == nil { res = prov.FloatGetter } + case "js": + var prov *Javascript + if prov, err = NewJavascriptProviderFromConfig(config.Other); err == nil { + res = prov.FloatGetter + } case "websocket", "ws": var prov *Socket if prov, err = NewSocketProviderFromConfig(config.Other); err == nil { @@ -115,6 +120,11 @@ func NewIntGetterFromConfig(config Config) (res func() (int64, error), err error if prov, err = NewHTTPProviderFromConfig(config.Other); err == nil { res = prov.IntGetter } + case "js": + var prov *Javascript + if prov, err = NewJavascriptProviderFromConfig(config.Other); err == nil { + res = prov.IntGetter + } case "websocket", "ws": var prov *Socket if prov, err = NewSocketProviderFromConfig(config.Other); err == nil { @@ -159,6 +169,11 @@ func NewStringGetterFromConfig(config Config) (res func() (string, error), err e if prov, err = NewHTTPProviderFromConfig(config.Other); err == nil { res = prov.StringGetter } + case "js": + var prov *Javascript + if prov, err = NewJavascriptProviderFromConfig(config.Other); err == nil { + res = prov.StringGetter + } case "websocket", "ws": var prov *Socket if prov, err = NewSocketProviderFromConfig(config.Other); err == nil { @@ -200,6 +215,11 @@ func NewBoolGetterFromConfig(config Config) (res func() (bool, error), err error if prov, err = NewHTTPProviderFromConfig(config.Other); err == nil { res = prov.BoolGetter } + case "js": + var prov *Javascript + if prov, err = NewJavascriptProviderFromConfig(config.Other); err == nil { + res = prov.BoolGetter + } case "websocket", "ws": var prov *Socket if prov, err = NewSocketProviderFromConfig(config.Other); err == nil { diff --git a/provider/http.go b/provider/http.go index 07fc36a2f..173138174 100644 --- a/provider/http.go +++ b/provider/http.go @@ -51,7 +51,9 @@ func NewHTTPProviderFromConfig(other map[string]interface{}) (*HTTP, error) { Scale float64 Insecure bool Auth Auth - }{Headers: make(map[string]string)} + }{ + Headers: make(map[string]string), + } if err := util.DecodeOther(other, &cc); err != nil { return nil, err diff --git a/provider/javascript.go b/provider/javascript.go new file mode 100644 index 000000000..4f61242ea --- /dev/null +++ b/provider/javascript.go @@ -0,0 +1,95 @@ +package provider + +import ( + "github.com/andig/evcc/util" + "github.com/robertkrimen/otto" +) + +// Javascript implements Javascript request provider +type Javascript struct { + log *util.Logger + vm *otto.Otto + script string +} + +// NewJavascriptProviderFromConfig creates a HTTP provider +func NewJavascriptProviderFromConfig(other map[string]interface{}) (*Javascript, error) { + cc := struct { + Script string + }{} + + if err := util.DecodeOther(other, &cc); err != nil { + return nil, err + } + + log := util.NewLogger("js") + + p := &Javascript{ + log: log, + vm: otto.New(), + script: cc.Script, + } + + return p, nil +} + +// FloatGetter parses float from request +func (p *Javascript) FloatGetter() (res float64, err error) { + v, err := p.vm.Eval(p.script) + if err == nil { + res, err = v.ToFloat() + } + + return res, err +} + +// IntGetter parses int64 from request +func (p *Javascript) IntGetter() (res int64, err error) { + v, err := p.vm.Eval(p.script) + if err == nil { + res, err = v.ToInteger() + } + + return res, err +} + +// StringGetter sends string request +func (p *Javascript) StringGetter() (res string, err error) { + v, err := p.vm.Eval(p.script) + if err == nil { + res, err = v.ToString() + } + + return res, err +} + +// BoolGetter parses bool from request +func (p *Javascript) BoolGetter() (res bool, err error) { + v, err := p.vm.Eval(p.script) + if err == nil { + res, err = v.ToBoolean() + } + + return res, err +} + +// // IntSetter sends int request +// func (p *Javascript) IntSetter(param int64) error { +// body := util.FormatValue(p.body, param) +// _, err := p.request(body) +// return err +// } + +// // StringSetter sends string request +// func (p *Javascript) StringSetter(param string) error { +// body := util.FormatValue(p.body, param) +// _, err := p.request(body) +// return err +// } + +// // BoolSetter sends bool request +// func (p *Javascript) BoolSetter(param bool) error { +// body := util.FormatValue(p.body, param) +// _, err := p.request(body) +// return err +// }