Skip to content

Commit

Permalink
Add javascript plugin (#510)
Browse files Browse the repository at this point in the history
  • Loading branch information
andig committed Dec 8, 2020
1 parent 6724f04 commit 3455042
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 3 deletions.
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
)

Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
20 changes: 20 additions & 0 deletions provider/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion provider/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
95 changes: 95 additions & 0 deletions provider/javascript.go
Original file line number Diff line number Diff line change
@@ -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
// }

0 comments on commit 3455042

Please sign in to comment.