Merge pull request #575 from andig/feature/gokrazy-config
Switch into demo mode when config file not available
This commit is contained in:
commit
6da99eb8dd
12 changed files with 257 additions and 12 deletions
|
|
@ -24,7 +24,10 @@ func runCharger(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s (%s)", server.Version, server.Commit)
|
||||
|
||||
// load config
|
||||
conf := loadConfigFile(cfgFile)
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
// setup mqtt
|
||||
if conf.Mqtt.Broker != "" {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
140
cmd/demo.go
Normal file
140
cmd/demo.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
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
|
||||
vm: shared
|
||||
script: state.gridpower = state.pvpower + state.chargepower + state.residualpower + state.batterypower;
|
||||
|
||||
- name: pv
|
||||
type: default
|
||||
power:
|
||||
type: js
|
||||
vm: shared
|
||||
script: state.pvpower += 100*Math.random();
|
||||
|
||||
- name: battery
|
||||
type: default
|
||||
power:
|
||||
type: js
|
||||
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:
|
||||
- name: demo
|
||||
type: default
|
||||
enable:
|
||||
type: js
|
||||
vm: shared
|
||||
script: |
|
||||
set();
|
||||
state.enabled = val;
|
||||
if (state.enabled) state.chargepower = state.maxcurrent * 230; else state.chargepower = 0;
|
||||
enabled:
|
||||
type: js
|
||||
vm: shared
|
||||
script: |
|
||||
state.enabled;
|
||||
status:
|
||||
type: js
|
||||
vm: shared
|
||||
script: |
|
||||
if (state.enabled) "C"; else "B";
|
||||
maxcurrent:
|
||||
type: js
|
||||
vm: shared
|
||||
script: |
|
||||
set();
|
||||
state.maxcurrent = val;
|
||||
if (state.enabled) state.chargepower = state.maxcurrent * 230;
|
||||
|
||||
vehicles:
|
||||
- name: demo
|
||||
title: e-Golf
|
||||
type: default
|
||||
charge:
|
||||
type: js
|
||||
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
|
||||
meters:
|
||||
grid: grid
|
||||
pv: pv
|
||||
battery: battery
|
||||
|
||||
loadpoints:
|
||||
- title: Carport
|
||||
charger: demo
|
||||
meters:
|
||||
charge: charge
|
||||
vehicle: demo
|
||||
`
|
||||
|
||||
func demoConfig() (conf config) {
|
||||
demo := map[string]interface{}{}
|
||||
|
||||
if err := yaml.Unmarshal([]byte(demoYaml), &demo); err != nil {
|
||||
log.FATAL.Fatalf("failed decoding demo config: %+v", err)
|
||||
}
|
||||
|
||||
for k, v := range demo {
|
||||
viper.Set(k, v)
|
||||
}
|
||||
|
||||
if err := viper.UnmarshalExact(&conf); err != nil {
|
||||
log.FATAL.Fatalf("failed loading demo config: %v", err)
|
||||
}
|
||||
|
||||
return conf
|
||||
}
|
||||
|
|
@ -26,7 +26,10 @@ func runDump(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s (%s)", server.Version, server.Commit)
|
||||
|
||||
// load config
|
||||
conf := loadConfigFile(cfgFile)
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
// setup mqtt
|
||||
if conf.Mqtt.Broker != "" {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ func runMeter(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s (%s)", server.Version, server.Commit)
|
||||
|
||||
// load config
|
||||
conf := loadConfigFile(cfgFile)
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
// setup mqtt
|
||||
if conf.Mqtt.Broker != "" {
|
||||
|
|
|
|||
10
cmd/root.go
10
cmd/root.go
|
|
@ -142,7 +142,12 @@ func run(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s (%s)", server.Version, server.Commit)
|
||||
|
||||
// load config and re-configure logging after reading config file
|
||||
conf := loadConfigFile(cfgFile)
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
log.ERROR.Println("missing evcc config - switching into demo mode")
|
||||
conf = demoConfig()
|
||||
}
|
||||
|
||||
util.LogLevel(viper.GetString("log"), viper.GetStringMapString("levels"))
|
||||
|
||||
uri := viper.GetString("uri")
|
||||
|
|
@ -153,6 +158,9 @@ func run(cmd *cobra.Command, args []string) {
|
|||
configureMQTT(conf.Mqtt)
|
||||
}
|
||||
|
||||
// setup javascript VMs
|
||||
configureJavascript(conf.Javascript)
|
||||
|
||||
// start broadcasting values
|
||||
tee := &Tee{}
|
||||
|
||||
|
|
|
|||
14
cmd/setup.go
14
cmd/setup.go
|
|
@ -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)
|
||||
|
|
@ -131,15 +139,15 @@ func configureLoadPoints(conf config, cp *ConfigProvider) (loadPoints []*core.Lo
|
|||
return loadPoints, nil
|
||||
}
|
||||
|
||||
func loadConfigFile(cfgFile string) (conf config) {
|
||||
func loadConfigFile(cfgFile string) (conf config, err error) {
|
||||
if cfgFile != "" {
|
||||
log.INFO.Println("using config file", cfgFile)
|
||||
if err := viper.UnmarshalExact(&conf); err != nil {
|
||||
log.FATAL.Fatalf("failed parsing config file %s: %v", cfgFile, err)
|
||||
}
|
||||
} else {
|
||||
log.FATAL.Fatal("missing evcc config")
|
||||
err = errors.New("missing evcc config")
|
||||
}
|
||||
|
||||
return conf
|
||||
return conf, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ func runVehicle(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s (%s)", server.Version, server.Commit)
|
||||
|
||||
// load config
|
||||
conf := loadConfigFile(cfgFile)
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
// setup mqtt
|
||||
if conf.Mqtt.Broker != "" {
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -57,6 +57,7 @@ require (
|
|||
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c
|
||||
github.com/volkszaehler/mbmd v0.0.0-20201205173745-5106cb0b334e
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0
|
||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -435,6 +435,8 @@ golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 h1:sIky/MyNRSHTrdxfsiUSS4WIA
|
|||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 h1:joucsQqXmyBVxViHCPFjG3hx8JzIFSaym3l3MM/Jsdg=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
56
provider/javascript/init.go
Normal file
56
provider/javascript/init.go
Normal 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue