forked from evcc-io/evcc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Experimentelle geführte Erstellung der Konfigurationsdatei (evcc-io#1888
) Dies ist eine experimentelle Umsetzung im folgenden aufgeführten Ziele. Wie es verwendet: - Start über `evcc configure` - Die Konfiguration wird in evcc.yaml geschrieben. Falls diese existiert kann ein alternativer Dateiname angegeben werden Was es kann: - Eine geführte Erstellung der Konfigurationsdatei - Direktes Testen ob die Konfiguration jedes Gerätes auch funktioniert - Konfigurationsabhängigkeiten durch direkte Konfiguration der Abhängigkeit lösen (z.B. Sponsorshipt required) - Konfiguration in für Anwender bekannte Informationen und möglichst wenig Informationseingabe zu ermöglichen, z.B. anhand von Produkten anstatt aus der Implementierungssicht - Bisherige Konfiguration funktioniert weiter
- Loading branch information
1 parent
3c311b6
commit 7e41a97
Showing
37 changed files
with
2,951 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ __debug_bin | |
*.log | ||
*.json | ||
*.yaml | ||
!templates/**/*.yaml | ||
!package*.json | ||
!evcc.dist.yaml | ||
evcc | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package charger | ||
|
||
import ( | ||
"github.com/evcc-io/evcc/api" | ||
"github.com/evcc-io/evcc/util" | ||
"github.com/evcc-io/evcc/util/templates" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
func init() { | ||
registry.Add("template", NewChargerFromTemplateConfig) | ||
} | ||
|
||
func NewChargerFromTemplateConfig(other map[string]interface{}) (api.Charger, error) { | ||
cc := struct { | ||
Template string | ||
Other map[string]interface{} `mapstructure:",remain"` | ||
}{} | ||
|
||
if err := util.DecodeOther(other, &cc); err != nil { | ||
return nil, err | ||
} | ||
|
||
tmpl, err := templates.ByTemplate(cc.Template, templates.Charger) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
b, _, err := tmpl.RenderResult(false, other) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var instance struct { | ||
Type string | ||
Other map[string]interface{} `yaml:",inline"` | ||
} | ||
|
||
if err := yaml.Unmarshal(b, &instance); err != nil { | ||
return nil, err | ||
} | ||
|
||
return NewFromConfig(instance.Type, instance.Other) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package charger | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/evcc-io/evcc/util/templates" | ||
"github.com/evcc-io/evcc/util/test" | ||
"github.com/thoas/go-funk" | ||
) | ||
|
||
func TestChargerTemplates(t *testing.T) { | ||
test.SkipCI(t) | ||
|
||
for _, tmpl := range templates.ByClass(templates.Charger) { | ||
tmpl := tmpl | ||
|
||
// set default values for all params | ||
values := tmpl.Defaults(true) | ||
|
||
// set the template value which is needed for rendering | ||
values["template"] = tmpl.Template | ||
|
||
// set modbus default test values | ||
if values[templates.ParamModbus] != nil { | ||
modbusChoices := tmpl.ModbusChoices() | ||
if funk.ContainsString(modbusChoices, templates.ModbusChoiceTCPIP) { | ||
values[templates.ModbusTCPIP] = true | ||
} else { | ||
values[templates.ModbusRS485TCPIP] = true | ||
} | ||
} | ||
|
||
t.Run(tmpl.Template, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
b, values, err := tmpl.RenderResult(true, values) | ||
if err != nil { | ||
t.Logf("%s: %s", tmpl.Template, b) | ||
t.Error(err) | ||
} | ||
|
||
_, err = NewFromConfig("template", values) | ||
if err != nil && !test.Acceptable(err, acceptable) { | ||
t.Logf("%s", tmpl.Template) | ||
t.Error(err) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package cmd | ||
|
||
import ( | ||
_ "embed" | ||
|
||
"github.com/evcc-io/evcc/cmd/configure" | ||
"github.com/evcc-io/evcc/util" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
// configureCmd represents the configure command | ||
var configureCmd = &cobra.Command{ | ||
Use: "configure", | ||
Short: "Create an EVCC configuration", | ||
Run: runConfigure, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(configureCmd) | ||
configureCmd.Flags().String("lang", "", "Define the localization to be used (en, de)") | ||
configureCmd.Flags().Bool("expand", false, "Enables rendering expanded configuration files") | ||
} | ||
|
||
func runConfigure(cmd *cobra.Command, args []string) { | ||
impl := &configure.CmdConfigure{} | ||
|
||
lang, err := cmd.Flags().GetString("lang") | ||
if err != nil { | ||
log.FATAL.Fatal(err) | ||
} | ||
|
||
expand, err := cmd.Flags().GetBool("expand") | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
util.LogLevel(viper.GetString("log"), nil) | ||
|
||
impl.Run(log, lang, expand) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package configure | ||
|
||
import ( | ||
"bytes" | ||
_ "embed" | ||
"fmt" | ||
"text/template" | ||
|
||
"github.com/Masterminds/sprig/v3" | ||
"github.com/thoas/go-funk" | ||
) | ||
|
||
type device struct { | ||
Name string | ||
Title string | ||
LogLevel string | ||
Yaml string | ||
ChargerHasMeter bool // only used with chargers to detect if we need to ask for a charge meter | ||
} | ||
|
||
type loadpoint struct { | ||
Title string // TODO Perspektivisch können wir was aus core wiederverwenden, für später | ||
Charger string | ||
ChargeMeter string | ||
Vehicles []string | ||
Mode string | ||
MinCurrent int | ||
MaxCurrent int | ||
Phases int | ||
} | ||
|
||
type config struct { | ||
Meters []device | ||
Chargers []device | ||
Vehicles []device | ||
Loadpoints []loadpoint | ||
Site struct { // TODO Perspektivisch können wir was aus core wiederverwenden, für später | ||
Title string | ||
Grid string | ||
PVs []string | ||
Batteries []string | ||
} | ||
LogLevels []string | ||
Hems string | ||
EEBUS string | ||
SponsorToken string | ||
} | ||
|
||
type Configure struct { | ||
config config | ||
} | ||
|
||
// AddLogLevel adds a log level for a specific device name to the configuration | ||
func (c *Configure) AddLogLevel(name string) { | ||
if name == "" || funk.ContainsString(c.config.LogLevels, name) { | ||
return | ||
} | ||
c.config.LogLevels = append(c.config.LogLevels, name) | ||
} | ||
|
||
// AddDevice adds a device reference of a specific category to the configuration | ||
// e.g. a PV meter to site.PVs | ||
func (c *Configure) AddDevice(d device, category DeviceCategory) { | ||
switch DeviceCategories[category].class { | ||
case DeviceClassCharger: | ||
c.AddLogLevel(d.LogLevel) | ||
if c.config.EEBUS != "" { | ||
c.AddLogLevel("eebus") | ||
} | ||
c.config.Chargers = append(c.config.Chargers, d) | ||
case DeviceClassMeter: | ||
c.AddLogLevel(d.LogLevel) | ||
c.config.Meters = append(c.config.Meters, d) | ||
switch DeviceCategories[category].categoryFilter { | ||
case DeviceCategoryGridMeter: | ||
c.config.Site.Grid = d.Name | ||
case DeviceCategoryPVMeter: | ||
c.config.Site.PVs = append(c.config.Site.PVs, d.Name) | ||
case DeviceCategoryBatteryMeter: | ||
c.config.Site.Batteries = append(c.config.Site.Batteries, d.Name) | ||
} | ||
case DeviceClassVehicle: | ||
c.AddLogLevel(d.LogLevel) | ||
c.config.Vehicles = append(c.config.Vehicles, d) | ||
} | ||
} | ||
|
||
// DevicesOfClass returns all configured devices of a given DeviceClass | ||
func (c *Configure) DevicesOfClass(class DeviceClass) []device { | ||
switch class { | ||
case DeviceClassCharger: | ||
return c.config.Chargers | ||
case DeviceClassMeter: | ||
return c.config.Meters | ||
case DeviceClassVehicle: | ||
return c.config.Vehicles | ||
} | ||
return nil | ||
} | ||
|
||
// AddLoadpoint adds a loadpoint to the configuration | ||
func (c *Configure) AddLoadpoint(l loadpoint) { | ||
c.config.Loadpoints = append(c.config.Loadpoints, l) | ||
c.AddLogLevel(fmt.Sprintf("lp-%d", 1+len(c.config.Loadpoints))) | ||
} | ||
|
||
// MetersOfCategory returns the number of configured meters of a given DeviceCategory | ||
func (c *Configure) MetersOfCategory(category DeviceCategory) int { | ||
switch category { | ||
case DeviceCategoryGridMeter: | ||
if c.config.Site.Grid != "" { | ||
return 1 | ||
} | ||
case DeviceCategoryPVMeter: | ||
return len(c.config.Site.PVs) | ||
case DeviceCategoryBatteryMeter: | ||
return len(c.config.Site.Batteries) | ||
} | ||
|
||
return 0 | ||
} | ||
|
||
//go:embed configure.tpl | ||
var configTmpl string | ||
|
||
// RenderConfiguration creates a yaml configuration | ||
func (c *Configure) RenderConfiguration() ([]byte, error) { | ||
tmpl, err := template.New("yaml").Funcs(template.FuncMap(sprig.FuncMap())).Parse(configTmpl) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
out := new(bytes.Buffer) | ||
err = tmpl.Execute(out, c.config) | ||
|
||
return bytes.TrimSpace(out.Bytes()), err | ||
} |
Oops, something went wrong.