cli configure updates (#1966)
- Add `--advanced` mode, where params defined as `advanced: true` will be asked - Add support for handling user input for lists, e.g. `identifiers` - Bool values are now a No/Yes selection instead of text input - Common definitions (params and render) are moved into single `parambaselist.yaml` file - Some refactoring
This commit is contained in:
parent
d538ec99f4
commit
4e119e8f6d
39 changed files with 325 additions and 355 deletions
|
|
@ -23,6 +23,7 @@ var acceptable = []string{
|
|||
"can only have either uri or device", // modbus
|
||||
"sponsorship required, see https://github.com/evcc-io/evcc#sponsorship",
|
||||
"eebus not configured",
|
||||
"unexpected status: 400", // easee
|
||||
}
|
||||
|
||||
func TestConfigChargers(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ var configureCmd = &cobra.Command{
|
|||
func init() {
|
||||
rootCmd.AddCommand(configureCmd)
|
||||
configureCmd.Flags().String("lang", "", "Define the localization to be used (en, de)")
|
||||
configureCmd.Flags().Bool("advanced", false, "Enables handling of advanced configuration options")
|
||||
configureCmd.Flags().Bool("expand", false, "Enables rendering expanded configuration files")
|
||||
}
|
||||
|
||||
|
|
@ -30,6 +31,11 @@ func runConfigure(cmd *cobra.Command, args []string) {
|
|||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
advanced, err := cmd.Flags().GetBool("advanced")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
expand, err := cmd.Flags().GetBool("expand")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -37,5 +43,5 @@ func runConfigure(cmd *cobra.Command, args []string) {
|
|||
|
||||
util.LogLevel(viper.GetString("log"), nil)
|
||||
|
||||
impl.Run(log, lang, expand)
|
||||
impl.Run(log, lang, advanced, expand)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import (
|
|||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func (c *CmdConfigure) configureDeviceGuidedSetup() {
|
|||
// we only ask for the configuration for the first usage
|
||||
deviceCategory = supportedDeviceCategories[0]
|
||||
|
||||
values = c.processConfig(templateItem.Params, deviceCategory, false)
|
||||
values = c.processConfig(templateItem.Params, deviceCategory)
|
||||
|
||||
deviceItem, err = c.processDeviceValues(values, templateItem, deviceItem, deviceCategory)
|
||||
if err != nil {
|
||||
|
|
@ -120,7 +120,7 @@ func (c *CmdConfigure) configureLinkedTypes(templateItem templates.Template) {
|
|||
break
|
||||
}
|
||||
|
||||
values := c.processConfig(linkedTemplateItem.Params, category, false)
|
||||
values := c.processConfig(linkedTemplateItem.Params, category)
|
||||
deviceItem, err = c.processDeviceValues(values, linkedTemplateItem, deviceItem, category)
|
||||
if err != nil {
|
||||
if !errors.Is(err, c.errDeviceNotValid) {
|
||||
|
|
@ -164,7 +164,7 @@ func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (d
|
|||
}
|
||||
|
||||
deviceDescription = templateItem.Description
|
||||
values := c.processConfig(templateItem.Params, deviceCategory, false)
|
||||
values := c.processConfig(templateItem.Params, deviceCategory)
|
||||
|
||||
device, err = c.processDeviceValues(values, templateItem, device, deviceCategory)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ func (c *CmdConfigure) processDeviceValues(values map[string]interface{}, templa
|
|||
|
||||
device.Name = fmt.Sprintf("%s%d", DeviceCategories[deviceCategory].defaultName, c.addedDeviceIndex)
|
||||
device.Title = templateItem.Description
|
||||
device.LogLevel = templateItem.LogLevel
|
||||
for item, value := range values {
|
||||
if strings.ToLower(item) != "title" {
|
||||
continue
|
||||
|
|
@ -233,120 +232,158 @@ func (c *CmdConfigure) paramChoiceValues(params []templates.Param, name string)
|
|||
// processConfig processes an EVCC configuration item
|
||||
// Returns:
|
||||
// a map with param name and values
|
||||
func (c *CmdConfigure) processConfig(paramItems []templates.Param, deviceCategory DeviceCategory, includeAdvanced bool) map[string]interface{} {
|
||||
func (c *CmdConfigure) processConfig(paramItems []templates.Param, deviceCategory DeviceCategory) map[string]interface{} {
|
||||
usageFilter := DeviceCategories[deviceCategory].categoryFilter
|
||||
|
||||
additionalConfig := make(map[string]interface{})
|
||||
selectedModbusKey := ""
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println(c.localizedString("Config_Title", nil))
|
||||
fmt.Println()
|
||||
|
||||
for _, param := range paramItems {
|
||||
if param.Name == templates.ParamModbus {
|
||||
// baudrate and comset defaults can be overwritten, as they are device specific
|
||||
deviceDefaultBaudrate := templates.ModbusParamValueBaudrate
|
||||
if param.Baudrate != 0 {
|
||||
deviceDefaultBaudrate = param.Baudrate
|
||||
switch param.Name {
|
||||
case templates.ParamModbus:
|
||||
c.processModbusConfig(param, deviceCategory, additionalConfig)
|
||||
case templates.ParamUsage:
|
||||
if usageFilter != "" {
|
||||
additionalConfig[param.Name] = usageFilter.String()
|
||||
}
|
||||
deviceDefaultComset := templates.ModbusParamValueComset
|
||||
if param.Comset != "" {
|
||||
deviceDefaultBaudrate = param.Baudrate
|
||||
}
|
||||
|
||||
choices := []string{}
|
||||
choiceKeys := []string{}
|
||||
for _, choice := range param.Choice {
|
||||
switch choice {
|
||||
case templates.ModbusChoiceRS485:
|
||||
choices = append(choices, "Serial (USB-RS485 Adapter)")
|
||||
choiceKeys = append(choiceKeys, templates.ModbusKeyRS485Serial)
|
||||
choices = append(choices, "Serial (Ethernet-RS485 Adapter)")
|
||||
choiceKeys = append(choiceKeys, templates.ModbusKeyRS485TCPIP)
|
||||
case templates.ModbusChoiceTCPIP:
|
||||
choices = append(choices, "TCP/IP")
|
||||
choiceKeys = append(choiceKeys, templates.ModbusKeyTCPIP)
|
||||
}
|
||||
}
|
||||
|
||||
if len(choices) > 0 {
|
||||
// ask for modbus address
|
||||
id := c.askValue(question{
|
||||
label: "ID",
|
||||
help: "Modbus ID",
|
||||
defaultValue: 1,
|
||||
valueType: templates.ParamValueTypeNumber,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNameId] = id
|
||||
|
||||
// ask for modbus interface type
|
||||
index := 0
|
||||
if len(choices) > 1 {
|
||||
index, _ = c.askChoice(c.localizedString("Config_ModbusInterface", nil), choices)
|
||||
}
|
||||
selectedModbusKey = choiceKeys[index]
|
||||
switch selectedModbusKey {
|
||||
case templates.ModbusKeyRS485Serial:
|
||||
device := c.askValue(question{
|
||||
label: "Device",
|
||||
help: "USB-RS485 Adapter Adresse",
|
||||
exampleValue: templates.ModbusParamValueDevice,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNameDevice] = device
|
||||
|
||||
baudrate := c.askValue(question{
|
||||
label: "Baudrate",
|
||||
defaultValue: deviceDefaultBaudrate,
|
||||
valueType: templates.ParamValueTypeNumber,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNameBaudrate] = baudrate
|
||||
|
||||
comset := c.askValue(question{
|
||||
label: "ComSet",
|
||||
defaultValue: deviceDefaultComset,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNameComset] = comset
|
||||
|
||||
case templates.ModbusKeyRS485TCPIP, templates.ModbusKeyTCPIP:
|
||||
if selectedModbusKey == templates.ModbusKeyRS485TCPIP {
|
||||
additionalConfig[templates.ModbusParamNameRTU] = "true"
|
||||
}
|
||||
host := c.askValue(question{
|
||||
label: "Host",
|
||||
exampleValue: templates.ModbusParamValueHost,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNameHost] = host
|
||||
|
||||
port := c.askValue(question{
|
||||
label: "Port",
|
||||
defaultValue: templates.ModbusParamValuePort,
|
||||
valueType: templates.ParamValueTypeNumber,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNamePort] = port
|
||||
}
|
||||
}
|
||||
} else if param.Name != templates.ParamUsage {
|
||||
if !includeAdvanced && param.Advanced {
|
||||
default:
|
||||
if !c.advancedMode && param.Advanced {
|
||||
continue
|
||||
}
|
||||
|
||||
userFriendly := c.userFriendlyTexts(param)
|
||||
value := c.askValue(question{
|
||||
label: userFriendly.Name,
|
||||
defaultValue: userFriendly.Default,
|
||||
exampleValue: userFriendly.Example,
|
||||
help: userFriendly.Help.String(c.lang),
|
||||
valueType: userFriendly.ValueType,
|
||||
mask: userFriendly.Mask,
|
||||
required: userFriendly.Required})
|
||||
additionalConfig[param.Name] = value
|
||||
} else if param.Name == templates.ParamUsage {
|
||||
if usageFilter != "" {
|
||||
additionalConfig[param.Name] = usageFilter.String()
|
||||
switch param.ValueType {
|
||||
case templates.ParamValueTypeStringList:
|
||||
additionalConfig[param.Name] = c.processListInputConfig(param)
|
||||
default:
|
||||
additionalConfig[param.Name] = c.processInputConfig(param)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return additionalConfig
|
||||
}
|
||||
|
||||
// handle user input of multiple items in a list
|
||||
func (c *CmdConfigure) processListInputConfig(param templates.Param) []string {
|
||||
var values []string
|
||||
|
||||
// ask for values until the decides stops
|
||||
for ok := true; ok; {
|
||||
newValue := c.processInputConfig(param)
|
||||
values = append(values, newValue)
|
||||
|
||||
if newValue == "" {
|
||||
break
|
||||
}
|
||||
|
||||
if !c.askYesNo(" " + c.localizedString("Config_AddAnotherValue", nil)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
// handle user input for a simple one value input
|
||||
func (c *CmdConfigure) processInputConfig(param templates.Param) string {
|
||||
userFriendly := c.userFriendlyTexts(param)
|
||||
return c.askValue(question{
|
||||
label: userFriendly.Name,
|
||||
defaultValue: userFriendly.Default,
|
||||
exampleValue: userFriendly.Example,
|
||||
help: userFriendly.Help.String(c.lang),
|
||||
valueType: userFriendly.ValueType,
|
||||
mask: userFriendly.Mask,
|
||||
required: userFriendly.Required})
|
||||
}
|
||||
|
||||
// handle user input for a device modbus configuration
|
||||
func (c *CmdConfigure) processModbusConfig(param templates.Param, deviceCategory DeviceCategory, additionalConfig map[string]interface{}) {
|
||||
var selectedModbusKey string
|
||||
|
||||
// baudrate and comset defaults can be overwritten, as they are device specific
|
||||
deviceDefaultBaudrate := templates.ModbusParamValueBaudrate
|
||||
if param.Baudrate != 0 {
|
||||
deviceDefaultBaudrate = param.Baudrate
|
||||
}
|
||||
deviceDefaultComset := templates.ModbusParamValueComset
|
||||
if param.Comset != "" {
|
||||
deviceDefaultBaudrate = param.Baudrate
|
||||
}
|
||||
|
||||
var choices []string
|
||||
var choiceKeys []string
|
||||
|
||||
for _, choice := range param.Choice {
|
||||
switch choice {
|
||||
case templates.ModbusChoiceRS485:
|
||||
choices = append(choices, "Serial (USB-RS485 Adapter)")
|
||||
choiceKeys = append(choiceKeys, templates.ModbusKeyRS485Serial)
|
||||
choices = append(choices, "Serial (Ethernet-RS485 Adapter)")
|
||||
choiceKeys = append(choiceKeys, templates.ModbusKeyRS485TCPIP)
|
||||
case templates.ModbusChoiceTCPIP:
|
||||
choices = append(choices, "TCP/IP")
|
||||
choiceKeys = append(choiceKeys, templates.ModbusKeyTCPIP)
|
||||
}
|
||||
}
|
||||
|
||||
if len(choices) > 0 {
|
||||
// ask for modbus address
|
||||
id := c.askValue(question{
|
||||
label: "ID",
|
||||
help: "Modbus ID",
|
||||
defaultValue: 1,
|
||||
valueType: templates.ParamValueTypeNumber,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNameId] = id
|
||||
|
||||
// ask for modbus interface type
|
||||
var index int
|
||||
if len(choices) > 1 {
|
||||
index, _ = c.askChoice(c.localizedString("Config_ModbusInterface", nil), choices)
|
||||
}
|
||||
selectedModbusKey = choiceKeys[index]
|
||||
switch selectedModbusKey {
|
||||
case templates.ModbusKeyRS485Serial:
|
||||
device := c.askValue(question{
|
||||
label: "Device",
|
||||
help: "USB-RS485 Adapter Adresse",
|
||||
exampleValue: templates.ModbusParamValueDevice,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNameDevice] = device
|
||||
|
||||
baudrate := c.askValue(question{
|
||||
label: "Baudrate",
|
||||
defaultValue: deviceDefaultBaudrate,
|
||||
valueType: templates.ParamValueTypeNumber,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNameBaudrate] = baudrate
|
||||
|
||||
comset := c.askValue(question{
|
||||
label: "ComSet",
|
||||
defaultValue: deviceDefaultComset,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNameComset] = comset
|
||||
|
||||
case templates.ModbusKeyRS485TCPIP, templates.ModbusKeyTCPIP:
|
||||
if selectedModbusKey == templates.ModbusKeyRS485TCPIP {
|
||||
additionalConfig[templates.ModbusParamNameRTU] = "true"
|
||||
}
|
||||
host := c.askValue(question{
|
||||
label: "Host",
|
||||
exampleValue: templates.ModbusParamValueHost,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNameHost] = host
|
||||
|
||||
port := c.askValue(question{
|
||||
label: "Port",
|
||||
defaultValue: templates.ModbusParamValuePort,
|
||||
valueType: templates.ParamValueTypeNumber,
|
||||
required: true})
|
||||
additionalConfig[templates.ModbusParamNamePort] = port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ Requirements_EEBUS_Pairing = "Du hast eine Wallbox ausgewählt, welche über das
|
|||
|
||||
Config_Title = "Führe folgende Einstellungen durch:"
|
||||
Config_ModbusInterface = "Wähle die ModBus Schnittstelle aus"
|
||||
Config_AddAnotherValue = "Möchtest du einen weiteren Wert hinzufügen?"
|
||||
Config_Yes = "Ja"
|
||||
Config_No = "Nein"
|
||||
|
||||
Cancel = "Die Konfiguration wurde abgebrochen.\n\nFalls diese geführte Konfiguration für dich noch nicht funktioniert, versuche es doch mal mit der manuellen Konfiguration. Details findest du auf der folgenden Webseite: https://docs.evcc.io/docs/installation/configuration\n"
|
||||
InputError = "Bei der Eingabe ist ein Fehler aufgetreten:"
|
||||
|
|
@ -82,10 +85,8 @@ InputError = "Bei der Eingabe ist ein Fehler aufgetreten:"
|
|||
Value_Required = "erforderlich"
|
||||
Value_Optional = "optional"
|
||||
Value_Sample = "Beispiel"
|
||||
Value_Bool = "'true' für ja oder 'false' für nein"
|
||||
ValueError_Used = "Dieser Wert wird bereits verwendet."
|
||||
ValueError_Empty = "Der Wert darf nicht leer sein."
|
||||
ValueError_Bool = "Der Wert muss 'true' (für ja) oder 'false' (für nein) sein."
|
||||
ValueError_Float = "Der Wert muss eine Zahl sein. Nachkommastellen mit . anstatt mit , trennen."
|
||||
ValueError_Number = "Der Wert muss eine ganzzahlige Zahl sein."
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ Requirements_EEBUS_Pairing = "You selected a wallbox, which will be access via t
|
|||
|
||||
Config_Title = "Please provide the following settings:"
|
||||
Config_ModbusInterface = "Choose the ModBus interface"
|
||||
Config_AddAnotherValue = "Do you want to add another value?"
|
||||
Config_Yes = "Yes"
|
||||
Config_No = "No"
|
||||
|
||||
Cancel = "The configuration was cancelled.\n\nIf this guided configuration process doesn't work for you yet, please try the manual configuration. You can find more details about that on our website: https://docs.evcc.io/docs/installation/configuration\n"
|
||||
InputError = "An input error occurred:"
|
||||
|
|
@ -82,10 +85,8 @@ InputError = "An input error occurred:"
|
|||
Value_Required = "required"
|
||||
Value_Optional = "optional"
|
||||
Value_Sample = "Example"
|
||||
Value_Bool = "'true' for yes or 'false' for no"
|
||||
ValueError_Used = "This value is aready in use."
|
||||
ValueError_Empty = "The value may not be empty."
|
||||
ValueError_Bool = "The value has to be 'true' (for yes) or 'false' (for no)."
|
||||
ValueError_Float = "The value has to be a number."
|
||||
ValueError_Number = "The value has to be an integer."
|
||||
|
||||
|
|
|
|||
|
|
@ -29,14 +29,15 @@ type CmdConfigure struct {
|
|||
log *util.Logger
|
||||
|
||||
lang string
|
||||
expandedMode bool
|
||||
advancedMode, expandedMode bool
|
||||
addedDeviceIndex int
|
||||
errItemNotPresent, errDeviceNotValid error
|
||||
}
|
||||
|
||||
// Run starts the interactive configuration
|
||||
func (c *CmdConfigure) Run(log *util.Logger, flagLang string, expandedMode bool) {
|
||||
func (c *CmdConfigure) Run(log *util.Logger, flagLang string, advancedMode, expandedMode bool) {
|
||||
c.log = log
|
||||
c.advancedMode = advancedMode
|
||||
c.expandedMode = expandedMode
|
||||
|
||||
c.log.INFO.Printf("evcc %s (%s)", server.Version, server.Commit)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
|
|
@ -118,8 +117,26 @@ type question struct {
|
|||
mask, required bool
|
||||
}
|
||||
|
||||
// askBoolValue asks for a boolean value selection for a given question
|
||||
func (c *CmdConfigure) askBoolValue(label string) string {
|
||||
choices := []string{c.localizedString("Config_No", nil), c.localizedString("Config_Yes", nil)}
|
||||
values := []string{"false", "true"}
|
||||
|
||||
index, _ := c.askChoice(label, choices)
|
||||
return values[index]
|
||||
}
|
||||
|
||||
// askValue asks for value input for a given question (template param)
|
||||
func (c *CmdConfigure) askValue(q question) string {
|
||||
if q.valueType == templates.ParamValueTypeBool {
|
||||
label := q.label
|
||||
if q.help != "" {
|
||||
label = q.help
|
||||
}
|
||||
|
||||
return c.askBoolValue(label)
|
||||
}
|
||||
|
||||
input := ""
|
||||
|
||||
var err error
|
||||
|
|
@ -134,12 +151,6 @@ func (c *CmdConfigure) askValue(q question) string {
|
|||
return errors.New(c.localizedString("ValueError_Empty", nil))
|
||||
}
|
||||
|
||||
if q.valueType == templates.ParamValueTypeBool {
|
||||
if strings.ToLower(value) != "true" && strings.ToLower(value) != "false" {
|
||||
return errors.New(c.localizedString("ValueError_Bool", nil))
|
||||
}
|
||||
}
|
||||
|
||||
if q.valueType == templates.ParamValueTypeFloat {
|
||||
_, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
|
|
@ -166,9 +177,6 @@ func (c *CmdConfigure) askValue(q question) string {
|
|||
if q.exampleValue != "" {
|
||||
help += fmt.Sprintf(" ("+c.localizedString("Value_Sample", nil)+": %s)", q.exampleValue)
|
||||
}
|
||||
if q.valueType == templates.ParamValueTypeBool {
|
||||
help += " (" + c.localizedString("Value_Bool", nil) + ")"
|
||||
}
|
||||
|
||||
if q.mask {
|
||||
prompt := &survey.Password{
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ func (c *CmdConfigure) userFriendlyTexts(param templates.Param) templates.Param
|
|||
if result.Help.String(c.lang) == "" {
|
||||
result.Help.SetString(c.lang, c.localizedString("UserFriendly_Vin_Help", nil))
|
||||
}
|
||||
case "identifier":
|
||||
case "identifiers":
|
||||
result.Name = c.localizedString("UserFriendly_Identifier_Name", nil)
|
||||
if result.Help.String(c.lang) == "" {
|
||||
result.Help.SetString(c.lang, c.localizedString("UserFriendly_Identifier_Help", nil))
|
||||
|
|
|
|||
|
|
@ -112,9 +112,14 @@ Allows to define a list of meter devices that are typically installed with this
|
|||
**Possible values**:
|
||||
|
||||
- `string`: for string values (default)
|
||||
- `bool`: for `true` and `false` values
|
||||
- `bool`: for `true` and `false` values. If `help` is provided, than that help text is presented as the question
|
||||
- `number`: for int values
|
||||
- `float`: for float values
|
||||
- `stringlist`: for a list of strings, e.g.used for defining a list of `identifiers` for `vehicles`
|
||||
|
||||
### `advanced`
|
||||
|
||||
`advanced` allows to specify if the param should only be asked if the cli is run with `--advanced`. Mostly used for non required params that are meant for users with advanced needs and knowledge.
|
||||
|
||||
### `help`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
template: pmcc
|
||||
description: Porsche Mobile Charger Connect
|
||||
loglevel: eebus
|
||||
requirements:
|
||||
eebus: true
|
||||
params:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
template: pmcp
|
||||
description: Porsche Mobile Charger Plus
|
||||
loglevel: eebus
|
||||
requirements:
|
||||
eebus: true
|
||||
params:
|
||||
|
|
|
|||
|
|
@ -5,4 +5,7 @@ import "embed"
|
|||
var (
|
||||
//go:embed charger/*.yaml meter/*.yaml vehicle/*.yaml
|
||||
YamlTemplates embed.FS
|
||||
|
||||
//go:embed parambaselist.yaml
|
||||
ParamBaseListDefinition string
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ template: sma-home-manager
|
|||
description: SMA Sunny Home Manager 2.0
|
||||
requirements:
|
||||
hems: sma
|
||||
loglevel: sma
|
||||
guidedsetup:
|
||||
enable: true
|
||||
linked:
|
||||
|
|
|
|||
36
templates/definition/parambaselist.yaml
Normal file
36
templates/definition/parambaselist.yaml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
vehicle:
|
||||
params:
|
||||
- name: title
|
||||
- name: user
|
||||
required: true
|
||||
- name: password
|
||||
required: true
|
||||
mask: true
|
||||
- name: vin
|
||||
example: W...
|
||||
- name: capacity
|
||||
default: '50'
|
||||
valueType: float
|
||||
- name: identifiers
|
||||
advanced: true
|
||||
valueType: stringlist
|
||||
render: |
|
||||
{{define "vehicle-common"}}
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
{{- if ne .capacity "" }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- end }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{- if ne (len .identifiers) 0 }}
|
||||
identifiers:
|
||||
{{- range .identifiers }}
|
||||
- {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{end}}
|
||||
|
|
@ -8,12 +8,4 @@ params:
|
|||
default: 60
|
||||
render: |
|
||||
type: audi
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,4 @@ params:
|
|||
default: 65
|
||||
render: |
|
||||
type: bmw
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,4 @@ params:
|
|||
default: 50
|
||||
render: |
|
||||
type: carwings
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,4 @@ params:
|
|||
default: 50
|
||||
render: |
|
||||
type: citroen
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,4 @@ params:
|
|||
default: 50
|
||||
render: |
|
||||
type: ds
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,7 @@ params:
|
|||
default: 42
|
||||
render: |
|
||||
type: fiat
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
{{- if ne .pin "" }}
|
||||
pin: {{ .pin }} #mandatory to deep refresh SoC
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,4 @@ params:
|
|||
default: 10
|
||||
render: |
|
||||
type: ford
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,4 @@ params:
|
|||
default: 64
|
||||
render: |
|
||||
type: hyundai
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,4 @@ params:
|
|||
default: 64
|
||||
render: |
|
||||
type: kia
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,4 @@ params:
|
|||
default: 32
|
||||
render: |
|
||||
type: mini
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,4 @@ params:
|
|||
default: 60
|
||||
render: |
|
||||
type: nissan
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,4 @@ params:
|
|||
default: 50
|
||||
render: |
|
||||
type: opel
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,4 @@ params:
|
|||
default: 50
|
||||
render: |
|
||||
type: peugeot
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,9 @@
|
|||
template: porsche
|
||||
description: Porsche
|
||||
loglevel: porsche
|
||||
paramsbase: vehicle
|
||||
params:
|
||||
- name: capacity
|
||||
default: 83.4
|
||||
render: |
|
||||
type: porsche
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
{{- if .capacity }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- end }}
|
||||
{{- if .vin }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,4 @@ params:
|
|||
default: 60
|
||||
render: |
|
||||
type: renault
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,4 @@ params:
|
|||
default: 10
|
||||
render: |
|
||||
type: seat
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
{{- if ne .capacity "" }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- end }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,4 @@ params:
|
|||
default: 10
|
||||
render: |
|
||||
type: skoda
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
{{- if ne .capacity "" }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- end }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,4 @@ params:
|
|||
default: 50
|
||||
render: |
|
||||
type: enyaq
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
{{- if ne .capacity "" }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- end }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,4 @@ params:
|
|||
default: 50
|
||||
render: |
|
||||
type: volvo
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
{{- if ne .capacity "" }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- end }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,4 @@ params:
|
|||
default: 10
|
||||
render: |
|
||||
type: vw
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
{{- if ne .capacity "" }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- end }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
template: id
|
||||
description: Volkswagen (We Connect ID)
|
||||
loglevel: id
|
||||
paramsbase: vehicle
|
||||
params:
|
||||
- name: vin
|
||||
|
|
@ -9,14 +8,4 @@ params:
|
|||
default: 50
|
||||
render: |
|
||||
type: id
|
||||
{{- if ne .title "" }}
|
||||
title: {{ .title }}
|
||||
{{- end }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
{{- if ne .capacity "" }}
|
||||
capacity: {{ .capacity }}
|
||||
{{- end }}
|
||||
{{- if ne .vin "" }}
|
||||
vin: {{ .vin }}
|
||||
{{- end }}
|
||||
{{include "vehicle-common" .}}
|
||||
|
|
|
|||
|
|
@ -7,4 +7,9 @@ description: {{ .Description }}
|
|||
{{ .Name }}:
|
||||
{{- if len .Value }} {{ .Value }} {{ end }}
|
||||
{{- if .Help.DE }} # {{ .Help.DE }} {{- end }}
|
||||
{{- if ne (len .Values) 0 }}
|
||||
{{- range .Values }}
|
||||
- {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end -}}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,13 @@ import (
|
|||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"github.com/evcc-io/evcc/templates/definition"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -45,10 +48,11 @@ const (
|
|||
var HemsValueTypes = []string{HemsTypeSMA}
|
||||
|
||||
const (
|
||||
ParamValueTypeString = "string"
|
||||
ParamValueTypeNumber = "number"
|
||||
ParamValueTypeFloat = "float"
|
||||
ParamValueTypeBool = "bool"
|
||||
ParamValueTypeString = "string"
|
||||
ParamValueTypeNumber = "number"
|
||||
ParamValueTypeFloat = "float"
|
||||
ParamValueTypeBool = "bool"
|
||||
ParamValueTypeStringList = "stringlist"
|
||||
)
|
||||
|
||||
var ParamValueTypes = []string{ParamValueTypeString, ParamValueTypeNumber, ParamValueTypeBool}
|
||||
|
|
@ -111,6 +115,7 @@ type Param struct {
|
|||
Help TextLanguage // cli configuration help
|
||||
Test string // testing default value
|
||||
Value string // user provided value via cli configuration
|
||||
Values []string // user provided list of values
|
||||
ValueType string // string representation of the value type, "string" is default
|
||||
Choice []string // defines which usage choices this config supports, valid elemtents are "grid", "pv", "battery", "charge"
|
||||
Usages []string
|
||||
|
|
@ -118,11 +123,17 @@ type Param struct {
|
|||
Comset string // device specific default for modbus RS485 comset
|
||||
}
|
||||
|
||||
type ParamBase struct {
|
||||
Params []Param
|
||||
Render string
|
||||
}
|
||||
|
||||
var paramBaseList map[string]ParamBase
|
||||
|
||||
// Template describes is a proxy device for use with cli and automated testing
|
||||
type Template struct {
|
||||
Template string
|
||||
Description string // user friendly description of the device this template describes
|
||||
LogLevel string // the implementation type of the device, equal to the type value under "Render"
|
||||
Requirements Requirements
|
||||
GuidedSetup GuidedSetup
|
||||
Generic bool // if this describes a generic device type rather than a product
|
||||
|
|
@ -131,31 +142,30 @@ type Template struct {
|
|||
Render string // rendering template
|
||||
}
|
||||
|
||||
var paramBases = map[string][]Param{
|
||||
"vehicle": {
|
||||
{Name: "title"},
|
||||
{Name: "user", Required: true},
|
||||
{Name: "password", Required: true, Mask: true},
|
||||
{Name: "vin", Example: "W..."},
|
||||
{Name: "capacity", Default: "50", ValueType: ParamValueTypeFloat},
|
||||
},
|
||||
}
|
||||
|
||||
// add the referenced base Params and overwrite existing ones
|
||||
func (t *Template) ResolveParamBase() {
|
||||
if t.ParamsBase == "" {
|
||||
return
|
||||
}
|
||||
|
||||
base, ok := paramBases[t.ParamsBase]
|
||||
if paramBaseList == nil {
|
||||
err := yaml.Unmarshal([]byte(definition.ParamBaseListDefinition), ¶mBaseList)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: failed to parse paramBasesDefinition: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
base, ok := paramBaseList[t.ParamsBase]
|
||||
if !ok {
|
||||
fmt.Printf("Error: Could not find parambase definition: %s\n", t.ParamsBase)
|
||||
return
|
||||
}
|
||||
|
||||
currentParams := make([]Param, len(t.Params))
|
||||
copy(currentParams, t.Params)
|
||||
t.Params = make([]Param, len(base))
|
||||
copy(t.Params, base)
|
||||
t.Params = make([]Param, len(base.Params))
|
||||
copy(t.Params, base.Params)
|
||||
for _, p := range currentParams {
|
||||
if i, item := t.paramWithName(p.Name); item != nil {
|
||||
// we only allow overwriting a few fields
|
||||
|
|
@ -175,12 +185,17 @@ func (t *Template) ResolveParamBase() {
|
|||
func (t *Template) Defaults(docsOrTests bool) map[string]interface{} {
|
||||
values := make(map[string]interface{})
|
||||
for _, p := range t.Params {
|
||||
if p.Test != "" {
|
||||
values[p.Name] = p.Test
|
||||
} else if p.Example != "" && docsOrTests {
|
||||
values[p.Name] = p.Example
|
||||
} else {
|
||||
values[p.Name] = p.Default // may be empty
|
||||
switch p.ValueType {
|
||||
case ParamValueTypeStringList:
|
||||
values[p.Name] = []string{}
|
||||
default:
|
||||
if p.Test != "" {
|
||||
values[p.Name] = p.Test
|
||||
} else if p.Example != "" && docsOrTests {
|
||||
values[p.Name] = p.Example
|
||||
} else {
|
||||
values[p.Name] = p.Default // may be empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,25 +245,39 @@ func (t *Template) RenderProxyWithValues(values map[string]interface{}, includeD
|
|||
|
||||
for index, p := range t.Params {
|
||||
for k, v := range values {
|
||||
if p.Name == k {
|
||||
t.Params[index].Value = v.(string)
|
||||
if p.Name != k {
|
||||
continue
|
||||
}
|
||||
|
||||
switch p.ValueType {
|
||||
case ParamValueTypeStringList:
|
||||
for _, e := range v.([]string) {
|
||||
t.Params[index].Values = append(p.Values, yamlQuote(e))
|
||||
}
|
||||
default:
|
||||
t.Params[index].Value = yamlQuote(v.(string))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove params with no values, no defaults and no example
|
||||
// remove params with no values
|
||||
var newParams []Param
|
||||
for _, param := range t.Params {
|
||||
if param.Value == "" && param.Default == "" && param.Example == "" && !param.Required {
|
||||
continue
|
||||
if !param.Required {
|
||||
switch param.ValueType {
|
||||
case ParamValueTypeStringList:
|
||||
if len(param.Values) == 0 {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
if param.Value == "" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
newParams = append(newParams, param)
|
||||
}
|
||||
|
||||
for index, p := range newParams {
|
||||
newParams[index].Value = yamlQuote(p.Value)
|
||||
}
|
||||
|
||||
t.Params = newParams
|
||||
|
||||
out := new(bytes.Buffer)
|
||||
|
|
@ -273,8 +302,24 @@ func (t *Template) RenderResult(docs bool, other map[string]interface{}) ([]byte
|
|||
|
||||
t.ModbusValues(values)
|
||||
|
||||
// add the common templates
|
||||
for _, v := range paramBaseList {
|
||||
if !strings.Contains(t.Render, v.Render) {
|
||||
t.Render = fmt.Sprintf("%s\n%s", t.Render, v.Render)
|
||||
}
|
||||
}
|
||||
|
||||
for item, p := range values {
|
||||
values[item] = yamlQuote(fmt.Sprintf("%v", p))
|
||||
switch p := p.(type) {
|
||||
case []string:
|
||||
var list []string
|
||||
for _, v := range p {
|
||||
list = append(list, yamlQuote(v))
|
||||
}
|
||||
values[item] = list
|
||||
default:
|
||||
values[item] = yamlQuote(fmt.Sprintf("%v", p))
|
||||
}
|
||||
}
|
||||
|
||||
tmpl := template.New("yaml")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue