evcc-io/cmd/configure/flow.go
Andreas Linde cfb174f9f5
Some checks failed
Default / Build (push) Failing after 2m0s
Default / Build UI (push) Failing after 13s
Release / Publish Docker :release (push) Has been cancelled
Release / call-build-workflow (push) Has been cancelled
Release / Publish Github & APT release (push) Has been cancelled
More template stuff (#2164)
- Configure now asks for the users knowledge level, so advanced mode can be reached without calling with `--advanced`
- Add template for solaredge inverter
- Add beta template for sungrow inverter
- Add template for MQTT based meter
- Add more template documentation
- Add check and error message for invalid config properties when using templates
- Option to specify language specific template descriptions
- Option to make params dependent on values of other params 
- Option to add sponsortoken in advanced mode
- Option to set minCurrent in advanced mode
- Remove sponsorship requirement from Daheimladen wallbox
- Multiple fixes (FritzDect meter, Kostsal Piko meter, ...)
2022-01-09 12:59:54 +01:00

236 lines
6.6 KiB
Go

package configure
import (
"errors"
"fmt"
"github.com/evcc-io/evcc/util/templates"
)
// configureDeviceGuidedSetup lets the user choose a device that is set to support guided setup
// these are typically devices that
// - contain multiple usages but have the same parameters like host, port, etc.
// - devices that typically are installed with additional specific devices (e.g. SMA Home Manager with SMA Inverters)
func (c *CmdConfigure) configureDeviceGuidedSetup() {
var err error
var values map[string]interface{}
var deviceCategory DeviceCategory
var supportedDeviceCategories []DeviceCategory
var templateItem templates.Template
deviceItem := device{}
for ok := true; ok; {
fmt.Println()
templateItem, err = c.processDeviceSelection(DeviceCategoryGuidedSetup)
if err != nil {
return
}
usageChoices := c.paramChoiceValues(templateItem.Params, templates.ParamUsage)
if len(usageChoices) == 0 {
panic("ERROR: Device template is missing valid usages!")
}
if len(usageChoices) == 0 {
usageChoices = []string{string(DeviceCategoryGridMeter), string(DeviceCategoryPVMeter), string(DeviceCategoryBatteryMeter)}
}
supportedDeviceCategories = []DeviceCategory{}
for _, usage := range usageChoices {
switch usage {
case string(DeviceCategoryGridMeter):
supportedDeviceCategories = append(supportedDeviceCategories, DeviceCategoryGridMeter)
case string(DeviceCategoryPVMeter):
supportedDeviceCategories = append(supportedDeviceCategories, DeviceCategoryPVMeter)
case string(DeviceCategoryBatteryMeter):
supportedDeviceCategories = append(supportedDeviceCategories, DeviceCategoryBatteryMeter)
}
}
// we only ask for the configuration for the first usage
deviceCategory = supportedDeviceCategories[0]
values = c.processConfig(templateItem, deviceCategory)
deviceItem, err = c.processDeviceValues(values, templateItem, deviceItem, deviceCategory)
if err != nil {
if err != c.errDeviceNotValid {
fmt.Println()
fmt.Println(err)
}
fmt.Println()
if !c.askConfigFailureNextStep() {
return
}
continue
}
break
}
c.configuration.AddDevice(deviceItem, deviceCategory)
if len(supportedDeviceCategories) > 1 {
for _, additionalCategory := range supportedDeviceCategories[1:] {
values[templates.ParamUsage] = additionalCategory.String()
deviceItem, err := c.processDeviceValues(values, templateItem, deviceItem, additionalCategory)
if err != nil {
continue
}
c.configuration.AddDevice(deviceItem, additionalCategory)
}
}
fmt.Println()
fmt.Println(templateItem.Description.String(c.lang) + " " + c.localizedString("Device_Added", nil))
c.configureLinkedTypes(templateItem)
}
// configureLinkedTypes lets the user configure devices that are marked as being linked to a guided device
// e.g. SMA Inverters, Energy Meter with SMA Home Manager
func (c *CmdConfigure) configureLinkedTypes(templateItem templates.Template) {
linkedTemplates := templateItem.GuidedSetup.Linked
deviceOfTemplateAdded := make(map[string]bool)
if linkedTemplates == nil {
return
}
for _, linkedTemplate := range linkedTemplates {
if linkedTemplate.ExcludeTemplate != "" {
// don't process this linked template if a referenced exclude template was added
if deviceOfTemplateAdded[linkedTemplate.ExcludeTemplate] {
continue
}
}
linkedTemplateItem, err := templates.ByTemplate(linkedTemplate.Template, string(DeviceClassMeter))
if err != nil {
fmt.Println("Error: " + err.Error())
return
}
if len(linkedTemplateItem.Params) == 0 || linkedTemplate.Usage == "" {
break
}
category := DeviceCategory(linkedTemplate.Usage)
localizeMap := localizeMap{
"Linked": linkedTemplateItem.Description.String(c.lang),
"Article": DeviceCategories[category].article,
"Additional": DeviceCategories[category].additional,
"Category": DeviceCategories[category].title,
}
fmt.Println()
if !c.askYesNo(c.localizedString("AddLinkedDeviceInCategory", localizeMap)) {
continue
}
for ok := true; ok; {
if added := c.configureLinkedTemplate(linkedTemplateItem, category); added {
deviceOfTemplateAdded[linkedTemplate.Template] = true
}
if !linkedTemplate.Multiple {
break
}
fmt.Println()
if !c.askYesNo(c.localizedString("AddAnotherLinkedDeviceInCategory", localizeMap)) {
break
}
}
}
}
// configureLinkedTemplate lets the user configure a device that is marked as being linked to a guided device
// returns true if a device was added
func (c *CmdConfigure) configureLinkedTemplate(template templates.Template, category DeviceCategory) bool {
for ok := true; ok; {
deviceItem := device{}
values := c.processConfig(template, category)
deviceItem, err := c.processDeviceValues(values, template, deviceItem, category)
if err != nil {
if !errors.Is(err, c.errDeviceNotValid) {
fmt.Println()
fmt.Println(err)
}
fmt.Println()
if c.askConfigFailureNextStep() {
continue
}
} else {
c.configuration.AddDevice(deviceItem, category)
fmt.Println()
fmt.Println(template.Description.String(c.lang) + " " + c.localizedString("Device_Added", nil))
return true
}
break
}
return false
}
// configureDeviceCategory lets the user select and configure a device from a specific category
func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (device, templates.Capabilities, error) {
fmt.Println()
fmt.Printf("- %s %s\n", c.localizedString("Device_Configure", nil), DeviceCategories[deviceCategory].title)
device := device{
Name: DeviceCategories[deviceCategory].defaultName,
}
var deviceDescription string
var capabilities templates.Capabilities
// repeat until the device is added or the user chooses to continue without adding a device
for ok := true; ok; {
fmt.Println()
templateItem, err := c.processDeviceSelection(deviceCategory)
if err != nil {
return device, capabilities, c.errItemNotPresent
}
deviceDescription = templateItem.Description.String(c.lang)
capabilities = templateItem.Capabilities
values := c.processConfig(templateItem, deviceCategory)
device, err = c.processDeviceValues(values, templateItem, device, deviceCategory)
if err != nil {
if err != c.errDeviceNotValid {
fmt.Println()
fmt.Println(err)
}
// ask if the user wants to add the
fmt.Println()
if !c.askConfigFailureNextStep() {
return device, capabilities, err
}
continue
}
break
}
c.configuration.AddDevice(device, deviceCategory)
var deviceTitle string
if device.Title != "" {
deviceTitle = " " + device.Title
}
fmt.Println()
fmt.Println(deviceDescription + deviceTitle + " " + c.localizedString("Device_Added", nil))
return device, capabilities, nil
}