Add shared javascript VMs and demo config

This commit is contained in:
andig 2021-01-04 16:18:05 +01:00
parent e767cf0fbc
commit 85dbee393f
6 changed files with 153 additions and 12 deletions

View file

@ -21,6 +21,7 @@ type config struct {
Levels map[string]string
Interval time.Duration
Mqtt mqttConfig
Javascript map[string]interface{}
Influx server.InfluxConfig
HEMS typedConfig
Messaging messagingConfig

View file

@ -8,24 +8,60 @@ import (
const demoYaml = `
log: info
javascript:
- vm: shared
script: |
state = {
residualpower: 200,
pvpower: -3000,
batterypower: 200,
gridpower: -2000,
chargepower: 0,
maxcurrent: 0,
battery: 63, // car
};
function get() {
console.log("state:", JSON.stringify(state));
}
function set() {
console.log(param+":", val);
console.log("state:", JSON.stringify(state));
}
meters:
- name: grid
type: default
power:
type: js
script: "1200"
vm: shared
script: state.gridpower = state.pvpower + state.chargepower + state.residualpower + state.batterypower;
- name: pv
type: default
power:
type: js
script: "6400"
vm: shared
script: state.pvpower += 100*Math.random();
- name: battery
type: default
power:
type: js
script: "200"
vm: shared
script: state.batterypower;
soc:
type: js
vm: shared
script: "30"
- name: charge
type: default
power:
type: js
vm: shared
script: state.chargepower;
soc:
type: js
vm: shared
script: "30"
chargers:
@ -33,16 +69,28 @@ chargers:
type: default
enable:
type: js
script: "true"
vm: shared
script: |
set();
state.enabled = val;
if (state.enabled) state.chargepower = state.maxcurrent * 230; else state.chargepower = 0;
enabled:
type: js
script: "true"
vm: shared
script: |
state.enabled;
status:
type: js
script: "'B'"
vm: shared
script: |
if (state.enabled) "C"; else "B";
maxcurrent:
type: js
script: "6"
vm: shared
script: |
set();
state.maxcurrent = val;
if (state.enabled) state.chargepower = state.maxcurrent * 230;
vehicles:
- name: demo
@ -50,7 +98,13 @@ vehicles:
type: default
charge:
type: js
script: "63"
vm: shared
script: |
if (state.chargepower > 0) state.battery++; else state.battery--;
if (state.battery < 15) state.battery = 15;
if (state.battery > 100) state.battery = 100;
state.battery;
cache: 1s
site:
title: Demo
@ -62,6 +116,8 @@ site:
loadpoints:
- title: Carport
charger: demo
meters:
charge: charge
vehicle: demo
`

View file

@ -158,6 +158,9 @@ func run(cmd *cobra.Command, args []string) {
configureMQTT(conf.Mqtt)
}
// setup javascript VMs
configureJavascript(conf.Javascript)
// start broadcasting values
tee := &Tee{}

View file

@ -9,6 +9,7 @@ import (
"github.com/andig/evcc/core"
"github.com/andig/evcc/hems"
"github.com/andig/evcc/provider/javascript"
"github.com/andig/evcc/provider/mqtt"
"github.com/andig/evcc/push"
"github.com/andig/evcc/server"
@ -57,6 +58,13 @@ func configureMQTT(conf mqttConfig) {
}
}
// setup javascript
func configureJavascript(conf map[string]interface{}) {
if err := javascript.Configure(conf); err != nil {
log.FATAL.Fatalf("failed configuring javascript: %v", err)
}
}
// setup HEMS
func configureHEMS(conf typedConfig, site *core.Site, cache *util.Cache, httpd *server.HTTPd) hems.HEMS {
hems, err := hems.NewFromConfig(conf.Type, conf.Other, site, cache, httpd)

View file

@ -1,6 +1,9 @@
package provider
import (
"strings"
"github.com/andig/evcc/provider/javascript"
"github.com/andig/evcc/util"
"github.com/robertkrimen/otto"
)
@ -19,6 +22,7 @@ func init() {
// NewJavascriptProviderFromConfig creates a HTTP provider
func NewJavascriptProviderFromConfig(other map[string]interface{}) (IntProvider, error) {
cc := struct {
VM string
Script string
}{}
@ -28,9 +32,11 @@ func NewJavascriptProviderFromConfig(other map[string]interface{}) (IntProvider,
log := util.NewLogger("js")
vm := javascript.RegisteredVM(strings.ToLower(cc.VM))
p := &Javascript{
log: log,
vm: otto.New(),
vm: vm,
script: cc.Script,
}
@ -85,10 +91,21 @@ func (p *Javascript) BoolGetter() func() (bool, error) {
}
}
func (p *Javascript) setParam(param string, val interface{}) error {
err := p.vm.Set(param, val)
if err == nil {
err = p.vm.Set("param", param)
}
if err == nil {
err = p.vm.Set("val", val)
}
return err
}
// IntSetter sends int request
func (p *Javascript) IntSetter(param string) func(int64) error {
return func(val int64) error {
err := p.vm.Set(param, val)
err := p.setParam(param, val)
if err == nil {
_, err = p.vm.Eval(p.script)
}
@ -99,7 +116,7 @@ func (p *Javascript) IntSetter(param string) func(int64) error {
// StringSetter sends string request
func (p *Javascript) StringSetter(param string) func(string) error {
return func(val string) error {
err := p.vm.Set(param, val)
err := p.setParam(param, val)
if err == nil {
_, err = p.vm.Eval(p.script)
}
@ -110,7 +127,7 @@ func (p *Javascript) StringSetter(param string) func(string) error {
// BoolSetter sends bool request
func (p *Javascript) BoolSetter(param string) func(bool) error {
return func(val bool) error {
err := p.vm.Set(param, val)
err := p.setParam(param, val)
if err == nil {
_, err = p.vm.Eval(p.script)
}

View file

@ -0,0 +1,56 @@
package javascript
import (
"github.com/andig/evcc/util"
"github.com/robertkrimen/otto"
)
// Configure initializes JS VMs
func Configure(other map[string]interface{}) error {
cc := []struct {
VM string
Script string
}{}
if err := util.DecodeOther(other, &cc); err != nil {
return err
}
// init all VMs that require it
for _, conf := range cc {
if conf.Script == "" {
continue
}
if _, ok := registry[conf.VM]; !ok {
vm := otto.New()
_, err := vm.Run(conf.Script)
if err != nil {
return err
}
registry[conf.VM] = vm
}
}
return nil
}
var registry = make(map[string]*otto.Otto)
// RegisteredVM returns a JS VM. If name is not empty, it will return a shared instance.
func RegisteredVM(name string) *otto.Otto {
vm, ok := registry[name]
// create new VM
if !ok {
vm = otto.New()
if name != "" {
registry[name] = vm
}
}
return vm
}