More template stuff (#2164)
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

- 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, ...)
This commit is contained in:
Andreas Linde 2022-01-09 12:59:54 +01:00 • committed by GitHub
parent 8ba710ff5f
commit cfb174f9f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
144 changed files with 935 additions and 265 deletions

View file

@ -26,7 +26,7 @@ func NewChargerFromTemplateConfig(other map[string]interface{}) (api.Charger, er
return nil, err
}
b, _, err := tmpl.RenderResult(false, other)
b, _, err := tmpl.RenderResult(templates.TemplateRenderModeInstance, other)
if err != nil {
return nil, err
}

View file

@ -35,7 +35,7 @@ func TestChargerTemplates(t *testing.T) {
tmpl := tmpl
// set default values for all params
values := tmpl.Defaults(true)
values := tmpl.Defaults(templates.TemplateRenderModeUnitTest)
// set the template value which is needed for rendering
values["template"] = tmpl.Template
@ -43,6 +43,7 @@ func TestChargerTemplates(t *testing.T) {
// set modbus default test values
if values[templates.ParamModbus] != nil {
modbusChoices := tmpl.ModbusChoices()
// we only test one modbus setup
if funk.ContainsString(modbusChoices, templates.ModbusChoiceTCPIP) {
values[templates.ModbusTCPIP] = true
} else {
@ -53,15 +54,17 @@ func TestChargerTemplates(t *testing.T) {
t.Run(tmpl.Template, func(t *testing.T) {
t.Parallel()
b, values, err := tmpl.RenderResult(true, values)
b, values, err := tmpl.RenderResult(templates.TemplateRenderModeUnitTest, values)
if err != nil {
t.Logf("%s: %s", tmpl.Template, b)
t.Logf("Template: %s", tmpl.Template)
t.Logf("%s", string(b))
t.Error(err)
}
_, err = NewFromConfig("template", values)
if err != nil && !test.Acceptable(err, acceptable) {
t.Logf("%s", tmpl.Template)
t.Logf("Template: %s", tmpl.Template)
t.Logf("%s", string(b))
t.Error(err)
}
})

View file

@ -40,6 +40,7 @@ type config struct {
}
Hems string
EEBUS string
MQTT string
SponsorToken string
}

View file

@ -49,7 +49,7 @@ func (d *DeviceTest) Test() (DeviceTestResult, error) {
// configure creates a configured device from a template so we can test it
func (d *DeviceTest) configure() (interface{}, error) {
b, _, err := d.Template.RenderResult(false, d.ConfigValues)
b, _, err := d.Template.RenderResult(templates.TemplateRenderModeInstance, d.ConfigValues)
if err != nil {
return nil, err
}

View file

@ -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)
values = c.processConfig(templateItem, deviceCategory)
deviceItem, err = c.processDeviceValues(values, templateItem, deviceItem, deviceCategory)
if err != nil {
@ -86,7 +86,7 @@ func (c *CmdConfigure) configureDeviceGuidedSetup() {
}
fmt.Println()
fmt.Println(templateItem.Description + " " + c.localizedString("Device_Added", nil))
fmt.Println(templateItem.Description.String(c.lang) + " " + c.localizedString("Device_Added", nil))
c.configureLinkedTypes(templateItem)
}
@ -122,7 +122,7 @@ func (c *CmdConfigure) configureLinkedTypes(templateItem templates.Template) {
category := DeviceCategory(linkedTemplate.Usage)
localizeMap := localizeMap{
"Linked": linkedTemplateItem.Description,
"Linked": linkedTemplateItem.Description.String(c.lang),
"Article": DeviceCategories[category].article,
"Additional": DeviceCategories[category].additional,
"Category": DeviceCategories[category].title,
@ -148,7 +148,6 @@ func (c *CmdConfigure) configureLinkedTypes(templateItem templates.Template) {
}
}
}
fmt.Println("DONE")
}
// configureLinkedTemplate lets the user configure a device that is marked as being linked to a guided device
@ -157,7 +156,7 @@ func (c *CmdConfigure) configureLinkedTemplate(template templates.Template, cate
for ok := true; ok; {
deviceItem := device{}
values := c.processConfig(template.Params, category)
values := c.processConfig(template, category)
deviceItem, err := c.processDeviceValues(values, template, deviceItem, category)
if err != nil {
if !errors.Is(err, c.errDeviceNotValid) {
@ -173,7 +172,7 @@ func (c *CmdConfigure) configureLinkedTemplate(template templates.Template, cate
c.configuration.AddDevice(deviceItem, category)
fmt.Println()
fmt.Println(template.Description + " " + c.localizedString("Device_Added", nil))
fmt.Println(template.Description.String(c.lang) + " " + c.localizedString("Device_Added", nil))
return true
}
break
@ -182,7 +181,7 @@ func (c *CmdConfigure) configureLinkedTemplate(template templates.Template, cate
}
// configureDeviceCategory lets the user select and configure a device from a specific category
func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (device, error) {
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)
@ -191,6 +190,7 @@ func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (d
}
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; {
@ -198,11 +198,12 @@ func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (d
templateItem, err := c.processDeviceSelection(deviceCategory)
if err != nil {
return device, c.errItemNotPresent
return device, capabilities, c.errItemNotPresent
}
deviceDescription = templateItem.Description
values := c.processConfig(templateItem.Params, deviceCategory)
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 {
@ -213,7 +214,7 @@ func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (d
// ask if the user wants to add the
fmt.Println()
if !c.askConfigFailureNextStep() {
return device, err
return device, capabilities, err
}
continue
}
@ -231,5 +232,5 @@ func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (d
fmt.Println()
fmt.Println(deviceDescription + deviceTitle + " " + c.localizedString("Device_Added", nil))
return device, nil
return device, capabilities, nil
}

View file

@ -5,6 +5,8 @@ import (
"sort"
"strings"
"github.com/evcc-io/evcc/provider/mqtt"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/sponsor"
"github.com/evcc-io/evcc/util/templates"
"github.com/thoas/go-funk"
@ -15,7 +17,7 @@ import (
func (c *CmdConfigure) processDeviceSelection(deviceCategory DeviceCategory) (templates.Template, error) {
templateItem := c.selectItem(deviceCategory)
if templateItem.Description == c.localizedString("ItemNotPresent", nil) {
if templateItem.Description.String(c.lang) == c.localizedString("ItemNotPresent", nil) {
return templateItem, c.errItemNotPresent
}
@ -31,7 +33,7 @@ func (c *CmdConfigure) processDeviceValues(values map[string]interface{}, templa
c.addedDeviceIndex++
device.Name = fmt.Sprintf("%s%d", DeviceCategories[deviceCategory].defaultName, c.addedDeviceIndex)
device.Title = templateItem.Description
device.Title = templateItem.Description.String(c.lang)
for item, value := range values {
if strings.ToLower(item) != "title" {
continue
@ -45,9 +47,9 @@ func (c *CmdConfigure) processDeviceValues(values map[string]interface{}, templa
fmt.Println()
if categoryWithUsage {
fmt.Println(c.localizedString("TestingDevice_TitleUsage", localizeMap{"Device": templateItem.Description, "Usage": deviceCategory.String()}))
fmt.Println(c.localizedString("TestingDevice_TitleUsage", localizeMap{"Device": templateItem.Description.String(c.lang), "Usage": deviceCategory.String()}))
} else {
fmt.Println(c.localizedString("TestingDevice_Title", localizeMap{"Device": templateItem.Description}))
fmt.Println(c.localizedString("TestingDevice_Title", localizeMap{"Device": templateItem.Description.String(c.lang)}))
}
deviceTest := DeviceTest{
@ -61,9 +63,9 @@ func (c *CmdConfigure) processDeviceValues(values map[string]interface{}, templa
fmt.Println(" ", c.localizedString("Error", localizeMap{"Error": err}))
fmt.Println()
question := c.localizedString("TestingDevice_AddFailed", localizeMap{"Device": templateItem.Description})
question := c.localizedString("TestingDevice_AddFailed", localizeMap{"Device": templateItem.Description.String(c.lang)})
if categoryWithUsage {
question = c.localizedString("TestingDevice_AddFailedUsage", localizeMap{"Device": templateItem.Description, "Usage": deviceCategory.String()})
question = c.localizedString("TestingDevice_AddFailedUsage", localizeMap{"Device": templateItem.Description.String(c.lang), "Usage": deviceCategory.String()})
}
if !c.askYesNo(question) {
c.addedDeviceIndex--
@ -77,7 +79,7 @@ func (c *CmdConfigure) processDeviceValues(values map[string]interface{}, templa
templateItem.Params = append(templateItem.Params, templates.Param{Name: "name", Value: device.Name})
if !c.expandedMode {
b, err := templateItem.RenderProxyWithValues(values, false)
b, err := templateItem.RenderProxyWithValues(values, c.lang, false)
if err != nil {
c.addedDeviceIndex--
return device, err
@ -91,7 +93,7 @@ func (c *CmdConfigure) processDeviceValues(values map[string]interface{}, templa
templateItem.Render = fmt.Sprintf("name: {{ .name }}\n%s", templateItem.Render)
}
}
b, _, err := templateItem.RenderResult(false, values)
b, _, err := templateItem.RenderResult(templates.TemplateRenderModeInstance, values)
if err != nil {
c.addedDeviceIndex--
return device, err
@ -115,21 +117,9 @@ func (c *CmdConfigure) processDeviceRequirements(templateItem templates.Template
// check if sponsorship is required
if templateItem.Requirements.Sponsorship && c.configuration.config.SponsorToken == "" {
fmt.Println("-- Sponsorship -----------------------------")
fmt.Println()
fmt.Println(c.localizedString("Requirements_Sponsorship_Title", nil))
fmt.Println()
if !c.askYesNo(c.localizedString("Requirements_Sponsorship_Token", nil)) {
return c.errItemNotPresent
if err := c.askSponsortoken(true); err != nil {
return err
}
sponsortoken := c.askValue(question{
label: c.localizedString("Requirements_Sponsorship_Token_Input", nil),
mask: true,
required: true})
c.configuration.config.SponsorToken = sponsortoken
sponsor.Subject = sponsortoken
fmt.Println()
fmt.Println("--------------------------------------------")
}
// check if we need to setup a HEMS
@ -140,6 +130,23 @@ func (c *CmdConfigure) processDeviceRequirements(templateItem templates.Template
}
}
// check if we need to setup an MQTT broker
if templateItem.Requirements.Mqtt {
if c.configuration.config.MQTT == "" {
mqttConfig, err := c.configureMQTT()
if err != nil {
return err
}
mqttYaml, err := yaml.Marshal(mqttConfig)
if err != nil {
return err
}
c.configuration.config.MQTT = string(mqttYaml)
}
}
// check if we need to setup an EEBUS HEMS
if templateItem.Requirements.Eebus {
if c.configuration.config.EEBUS == "" {
@ -173,12 +180,90 @@ func (c *CmdConfigure) processDeviceRequirements(templateItem templates.Template
return nil
}
func (c *CmdConfigure) askSponsortoken(required bool) error {
fmt.Println("-- Sponsorship -----------------------------")
if required {
fmt.Println()
fmt.Println(c.localizedString("Requirements_Sponsorship_Title", nil))
}
fmt.Println()
if !c.askYesNo(c.localizedString("Requirements_Sponsorship_Token", nil)) {
return c.errItemNotPresent
}
sponsortoken := c.askValue(question{
label: c.localizedString("Requirements_Sponsorship_Token_Input", nil),
mask: true,
required: true})
c.configuration.config.SponsorToken = sponsortoken
sponsor.Subject = sponsortoken
fmt.Println()
fmt.Println("--------------------------------------------")
return nil
}
func (c *CmdConfigure) configureMQTT() (map[string]interface{}, error) {
fmt.Println()
fmt.Println("-- MQTT Broker ----------------------------")
var err error
for ok := true; ok; {
fmt.Println()
host := c.askValue(question{
label: c.localizedString("UserFriendly_Host_Name", nil),
mask: false,
required: true})
port := c.askValue(question{
label: c.localizedString("UserFriendly_Port_Name", nil),
mask: false,
required: true})
user := c.askValue(question{
label: c.localizedString("UserFriendly_User_Name", nil),
mask: false,
required: false})
password := c.askValue(question{
label: c.localizedString("UserFriendly_Password_Name", nil),
mask: true,
required: false})
fmt.Println()
fmt.Println("-------------------------------------------")
broker := fmt.Sprintf("%s:%s", host, port)
mqttConfig := map[string]interface{}{
"broker": broker,
"user": user,
"password": password,
}
log := util.NewLogger("mqtt")
if mqtt.Instance, err = mqtt.RegisteredClient(log, broker, user, password, "", 1); err == nil {
return mqttConfig, nil
}
fmt.Println()
question := c.localizedString("TestingMQTTFailed", nil)
if !c.askYesNo(question) {
return nil, fmt.Errorf("failed configuring mqtt: %w", err)
}
}
return nil, fmt.Errorf("failed configuring mqtt: %w", err)
}
// fetchElements returns template items of a given class
func (c *CmdConfigure) fetchElements(deviceCategory DeviceCategory) []templates.Template {
var items []templates.Template
for _, tmpl := range templates.ByClass(DeviceCategories[deviceCategory].class.String()) {
if len(tmpl.Params) == 0 || len(tmpl.Description) == 0 {
if len(tmpl.Params) == 0 || len(tmpl.Description.String(c.lang)) == 0 {
continue
}
@ -202,7 +287,7 @@ func (c *CmdConfigure) fetchElements(deviceCategory DeviceCategory) []templates.
if !items[i].Generic && items[j].Generic {
return true
}
return strings.ToLower(items[i].Description) < strings.ToLower(items[j].Description)
return strings.ToLower(items[i].Description.String(c.lang)) < strings.ToLower(items[j].Description.String(c.lang))
})
return items
@ -233,7 +318,7 @@ 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) map[string]interface{} {
func (c *CmdConfigure) processConfig(templateItem templates.Template, deviceCategory DeviceCategory) map[string]interface{} {
usageFilter := DeviceCategories[deviceCategory].categoryFilter
additionalConfig := make(map[string]interface{})
@ -242,7 +327,33 @@ func (c *CmdConfigure) processConfig(paramItems []templates.Param, deviceCategor
fmt.Println(c.localizedString("Config_Title", nil))
fmt.Println()
for _, param := range paramItems {
for _, param := range templateItem.Params {
if param.Dependencies != nil {
valid := true
for _, dep := range param.Dependencies {
_, valueParam := templateItem.ParamByName(dep.Name)
if valueParam == nil {
break
}
value := valueParam.Value
switch dep.Check {
case templates.DependencyCheckEmpty:
valid = value == ""
case templates.DependencyCheckNotEmpty:
valid = value != ""
case templates.DependencyCheckEqual:
valid = value == dep.Value
}
if !valid {
break
}
}
if !valid {
continue
}
}
switch param.Name {
case templates.ParamModbus:
c.processModbusConfig(param, deviceCategory, additionalConfig)
@ -271,7 +382,7 @@ func (c *CmdConfigure) processConfig(paramItems []templates.Param, deviceCategor
func (c *CmdConfigure) processListInputConfig(param templates.Param) []string {
var values []string
// ask for values until the decides stops
// ask for values until the user decides to stop
for ok := true; ok; {
newValue := c.processInputConfig(param)
values = append(values, newValue)
@ -291,8 +402,15 @@ func (c *CmdConfigure) processListInputConfig(param templates.Param) []string {
// handle user input for a simple one value input
func (c *CmdConfigure) processInputConfig(param templates.Param) string {
userFriendly := c.userFriendlyTexts(param)
label := userFriendly.Name
langLabel := param.Description.String(c.lang)
if langLabel != "" {
label = langLabel
}
return c.askValue(question{
label: userFriendly.Name,
label: label,
defaultValue: userFriendly.Default,
exampleValue: userFriendly.Example,
help: userFriendly.Help.String(c.lang),

View file

@ -1,5 +1,9 @@
Intro = "Die nächsten Schritte führen durch die Einrichtung einer Konfigurationsdatei für evcc.\nBeachte dass dieser Prozess nicht alle möglichen Szenarien berücksichtigen kann.\nDurch Drücken von CTRL-C kann der Prozess abgebrochen werden.\n\nACHTUNG: Diese Funktionalität hat experimentellen Status!\n D.h. es kann möglich sein, dass die hiermit erstellte Konfigurationsdatei\n in einem Update nicht mehr funktionieren könnte und neu erzeugt werden müsste.\n Wir freuen uns auf euer Feedback auf https://github.com/evcc-io/evcc/discussions/\n\nAuf geht`s:"
Flow_Mode = "In welchem Modus soll die Konfiguration durchgeführt werden?"
Flow_Mode_Standard = "Standard Modus (So einfach und schnell wie möglich)"
Flow_Mode_Advanced = "Fortgeschrittener Modus (Detailliertere Fragen, erfordert jedoch technisches Know-How)"
Flow_Type = "Was möchtest du machen?"
Flow_Type_NewConfiguration = "Eine neue evcc Konfigurationsdatei erstellen"
Flow_Type_SingleDevice = "Ein einzelnes Gerät konfigurieren (muss manuell in eine Konfigurationsdatei eingetragen werden!)"
@ -90,6 +94,8 @@ TestingDevice_RepeatStep = "Möchtest du erneut ein Gerät aus der Liste auswäh
TestingDevice_AddFailed = "Der Test von {{ .Device }} ist fehlgeschlagen. Soll es trotzdem in die Konfiguration aufgenommen werden?"
TestingDevice_AddFailedUsage = "Der Test der {{ .Usage }} Konfiguration von {{ .Device }} ist fehlgeschlagen. Soll es trotzdem in die Konfiguration aufgenommen werden?"
TestingMQTTFailed = "Der Test der MQTT Konfiguration ist fehlgeschlagen. Möchten Sie die Konfiguration wiederholen?"
Requirements_Title = "Das Gerät muss die folgenden Voraussetzungen erfüllen:"
Requirements_More = "Weitere Informationen:"
@ -131,7 +137,8 @@ Loadpoint_ResetOnDisconnect = "Soll beim Abstecken des Ladekables von einem Fahr
Loadpoint_WallboxWOMeter = "Die Wallbox hat keinen Ladestromzähler. Hast du einen externen Zähler dafür installiert, der verwendet werden kann?"
Loadpoint_WallboxMaxPower = "Was ist die maximale Leistung, welche die Wallbox zur Verfügung stellen kann?"
Loadpoint_WallboxMaxAmperage = "Was ist die maximale Stromstärke, welche die Wallbox auf einer Phase zur Verfügung stellen kann?"
Loadpoint_WallboxMinAmperage = "Was ist die MINIMALE Stromstärke, welche die Wallbox auf einer Phase zur Verfügung stellen kann?"
Loadpoint_WallboxMaxAmperage = "Was ist die MAXIMALE Stromstärke, welche die Wallbox auf einer Phase zur Verfügung stellen kann?"
Loadpoint_WallboxPhases = "Mit wievielen Phasen ist die Wallbox angeschlossen?"
Loadpoint_WallboxPower36kW = "3,6 kW"
Loadpoint_WallboxPower11kW = "11 kW"

View file

@ -1,5 +1,9 @@
Intro = "The next steps will guide you through the creation of a configuration file for evcc.\nPlease note that this process cannot cover all possible scenarios.\nYou can cancel the process by pressing CTRL-C anytime.\n\nNOTE: This functionality is in an experimental state!\n This means that the created configuration file may not work\n in a future update of evcc and would need to be recreated.\n We are looking forward to your feedback at https://github.com/evcc-io/evcc/discussions/\n\nLet's go:"
Flow_Mode = "In which mode should this process guide you through the configuration?"
Flow_Mode_Standard = "Standard mode (As simple and quick as possible)"
Flow_Mode_Advanced = "Advanced mode (Ask more details, requires technical know-how)"
Flow_Type = "What do you want to do?"
Flow_Type_NewConfiguration = "Create a new evcc configuration file"
Flow_Type_SingleDevice = "Configure a single device (has to be added manually to a configuration file!)"
@ -90,6 +94,8 @@ TestingDevice_RepeatStep = "Do you want to repeat choosing a device and configur
TestingDevice_AddFailed = "Testing {{ .Device }} failed. Do you want to add it anyway?"
TestingDevice_AddFailedUsage = "Testing of the {{ .Usage }} configuration of {{ .Device }} failed. Do you want to add it anyway?"
TestingMQTTFailed = "Testing the MQTT configuration failed. Do you want to repeat its configuration?"
Requirements_Title = "The device needs to provide the following requirements:"
Requirements_More = "Additional information:"
@ -131,7 +137,8 @@ Loadpoint_ResetOnDisconnect = "Shall disconnecting the charging cable from the v
Loadpoint_WallboxWOMeter = "The wallbox does not provide power data. Do you have an external meter that can be used?"
Loadpoint_WallboxMaxPower = "What is the maximum power your wallbox can provide?"
Loadpoint_WallboxMaxAmperage = "What is the maximum amperage your wallbox can provide on a single phase?"
Loadpoint_WallboxMinAmperage = "What is the MINIMUM amperage your wallbox can provide on a single phase?"
Loadpoint_WallboxMaxAmperage = "What is the MAXIMUM amperage your wallbox can provide on a single phase?"
Loadpoint_WallboxPhases = "How many phases are connected to the wallbox?"
Loadpoint_WallboxPower36kW = "3.6 kW"
Loadpoint_WallboxPower11kW = "11 kW"

View file

@ -65,19 +65,31 @@ func (c *CmdConfigure) Run(log *util.Logger, flagLang string, advancedMode, expa
c.setDefaultTexts()
fmt.Println()
fmt.Println(c.localizedString("Intro", nil))
if !c.advancedMode {
// ask the user for his knowledge, so advanced mode can also be turned on this way
fmt.Println()
flowIndex, _ := c.askChoice(c.localizedString("Flow_Mode", nil), []string{
c.localizedString("Flow_Mode_Standard", nil),
c.localizedString("Flow_Mode_Advanced", nil),
})
if flowIndex == 1 {
c.advancedMode = true
}
}
if !c.advancedMode {
c.flowNewConfigFile()
return
}
fmt.Println()
fmt.Println(c.localizedString("Intro", nil))
flowChoices := []string{
flowIndex, _ := c.askChoice(c.localizedString("Flow_Type", nil), []string{
c.localizedString("Flow_Type_NewConfiguration", nil),
c.localizedString("Flow_Type_SingleDevice", nil),
}
fmt.Println()
flowIndex, _ := c.askChoice(c.localizedString("Flow_Type", nil), flowChoices)
})
switch flowIndex {
case 0:
c.flowNewConfigFile()
@ -143,6 +155,10 @@ func (c *CmdConfigure) flowNewConfigFile() {
c.configureLoadpoints()
c.configureSite()
if c.advancedMode && c.configuration.config.SponsorToken == "" {
_ = c.askSponsortoken(true)
}
yaml, err := c.configuration.RenderConfiguration()
if err != nil {
c.log.FATAL.Fatal(err)
@ -207,7 +223,7 @@ func (c *CmdConfigure) configureDevices(deviceCategory DeviceCategory, askAdding
}
for ok := true; ok; {
device, err := c.configureDeviceCategory(deviceCategory)
device, _, err := c.configureDeviceCategory(deviceCategory)
if err != nil {
break
}
@ -243,7 +259,7 @@ func (c *CmdConfigure) configureLoadpoints() {
MinCurrent: 6,
}
charger, err := c.configureDeviceCategory(DeviceCategoryCharger)
charger, capabilities, err := c.configureDeviceCategory(DeviceCategoryCharger)
if err != nil {
break
}
@ -253,7 +269,7 @@ func (c *CmdConfigure) configureLoadpoints() {
if !chargerHasMeter {
if c.askYesNo(c.localizedString("Loadpoint_WallboxWOMeter", nil)) {
chargeMeter, err := c.configureDeviceCategory(DeviceCategoryChargeMeter)
chargeMeter, _, err := c.configureDeviceCategory(DeviceCategoryChargeMeter)
if err == nil {
loadpoint.ChargeMeter = chargeMeter.Name
chargerHasMeter = true
@ -272,6 +288,34 @@ func (c *CmdConfigure) configureLoadpoints() {
}
}
var minValue int = 6
if capabilities.ISO151182 {
minValue = 2
}
if c.advancedMode {
minAmperage := c.askValue(question{
label: c.localizedString("Loadpoint_WallboxMinAmperage", nil),
valueType: templates.ParamValueTypeNumber,
minNumberValue: int64(minValue),
maxNumberValue: 32,
required: true})
loadpoint.MinCurrent, _ = strconv.Atoi(minAmperage)
maxAmperage := c.askValue(question{
label: c.localizedString("Loadpoint_WallboxMaxAmperage", nil),
valueType: templates.ParamValueTypeNumber,
minNumberValue: 6,
maxNumberValue: 32,
required: true})
loadpoint.MaxCurrent, _ = strconv.Atoi(maxAmperage)
if !chargerHasMeter {
phaseChoices := []string{"1", "2", "3"}
fmt.Println()
phaseIndex, _ := c.askChoice(c.localizedString("Loadpoint_WallboxPhases", nil), phaseChoices)
loadpoint.Phases = phaseIndex + 1
}
} else {
powerChoices := []string{
c.localizedString("Loadpoint_WallboxPower36kW", nil),
c.localizedString("Loadpoint_WallboxPower11kW", nil),
@ -280,7 +324,7 @@ func (c *CmdConfigure) configureLoadpoints() {
}
fmt.Println()
powerIndex, _ := c.askChoice(c.localizedString("Loadpoint_WallboxMaxPower", nil), powerChoices)
loadpoint.MinCurrent = 6
loadpoint.MinCurrent = minValue
switch powerIndex {
case 0:
loadpoint.MaxCurrent = 16
@ -301,7 +345,7 @@ func (c *CmdConfigure) configureLoadpoints() {
amperage := c.askValue(question{
label: c.localizedString("Loadpoint_WallboxMaxAmperage", nil),
valueType: templates.ParamValueTypeNumber,
minNumberValue: 6,
minNumberValue: int64(minValue),
maxNumberValue: 32,
required: true})
loadpoint.MaxCurrent, _ = strconv.Atoi(amperage)
@ -313,6 +357,7 @@ func (c *CmdConfigure) configureLoadpoints() {
loadpoint.Phases = phaseIndex + 1
}
}
}
fmt.Println()
loadpoint.Mode = c.askValue(question{valueType: templates.ParamValueTypeChargeModes, excludeNone: true})

View file

@ -64,15 +64,15 @@ func (c *CmdConfigure) askSelection(message string, items []string) (error, stri
// selectItem selects item from list
func (c *CmdConfigure) selectItem(deviceCategory DeviceCategory) templates.Template {
var emptyItem templates.Template
emptyItem.Description = c.localizedString("ItemNotPresent", nil)
emptyItem.Description.SetString(c.localizedString("ItemNotPresent", nil), c.lang)
elements := c.fetchElements(deviceCategory)
elements = append(elements, emptyItem)
var items []string
for _, item := range elements {
if item.Description != "" {
items = append(items, item.Description)
if item.Description.String(c.lang) != "" {
items = append(items, item.Description.String(c.lang))
}
}

View file

@ -26,7 +26,7 @@ func NewMeterFromTemplateConfig(other map[string]interface{}) (api.Meter, error)
return nil, err
}
b, _, err := tmpl.RenderResult(false, other)
b, _, err := tmpl.RenderResult(templates.TemplateRenderModeInstance, other)
if err != nil {
return nil, err
}

View file

@ -33,7 +33,7 @@ func TestMeterTemplates(t *testing.T) {
tmpl := tmpl
// set default values for all params
values := tmpl.Defaults(true)
values := tmpl.Defaults(templates.TemplateRenderModeUnitTest)
// set the template value which is needed for rendering
values["template"] = tmpl.Template
@ -41,10 +41,11 @@ func TestMeterTemplates(t *testing.T) {
// set modbus default test values
if values[templates.ParamModbus] != nil {
modbusChoices := tmpl.ModbusChoices()
// we only test one modbus setup
if funk.ContainsString(modbusChoices, templates.ModbusChoiceTCPIP) {
values[templates.ModbusKeyTCPIP] = true
values[templates.ModbusTCPIP] = true
} else {
values[templates.ModbusKeyRS485TCPIP] = true
values[templates.ModbusRS485TCPIP] = true
}
}
@ -70,14 +71,16 @@ func runTest(t *testing.T, tmpl templates.Template, values map[string]interface{
t.Run(tmpl.Template, func(t *testing.T) {
t.Parallel()
b, values, err := tmpl.RenderResult(true, values)
b, values, err := tmpl.RenderResult(templates.TemplateRenderModeUnitTest, values)
if err != nil {
t.Logf("%s: %s", tmpl.Template, b)
t.Logf("Template: %s", tmpl.Template)
t.Logf("%s", string(b))
t.Error(err)
}
if _, err := NewFromConfig("template", values); err != nil && !test.Acceptable(err, acceptable) {
t.Logf("%s", tmpl.Template)
t.Logf("Template: %s", tmpl.Template)
t.Logf("%s", string(b))
t.Error(err)
}
})

View file

@ -18,7 +18,7 @@ The following describes each possible element in a yaml file
## `description`
`description` expects a human readable description of the device, best language neutral and containing the name of the product
`description` expects a human readable description of the device, best containing the name of the product. Values are language specific names via `de`, `en`, `generic` (if string is language independent)
## `generic`
@ -60,6 +60,14 @@ Allows to define a list of meter devices that are typically installed with this
Example Use Case: With SMA Home Manager, there can be a SMA Energy Meter used for getting the PV generation or multiple SMA PV inverters. But never both together. So if the used added an SMA Energy Meter, then the flow shoudn't ask for SMA PV inverters.
## `capabilities`
`capabilities` provides an option to define special capabilities of the devie
**Possible Values**:
- `iso151182: true`: If the charger supports communicating via ISO15118-2
## `requirements`
`requirements` provides an option to define various requirements / dependencies that need to be setup
@ -95,6 +103,39 @@ Example Use Case: With SMA Home Manager, there can be a SMA Energy Meter used fo
- `usage`: specifies a list of meter classes, the device can be used for. Possible values are `grid`, `pv`, `battery`, and `charger`
- `modbus`: specifies that this device is accessed via modbus. It requires the `choice` property to have a list of possible interface values the device provides. These values can be `rs485` and `tcpip`. The command will use either to ask the appropriate questions and settings. The `render` section needs to include the string `{{include "modbus" .}}` in all places where the configuration needs modbus settings.
#### Modbus Options
- `id`: Device specific default for modbus ID
- `port`: Device specific default for modbus TCPIP port
- `baudrate`: Device specific default for modbus RS485 baudrate
- `comset`: Device specific default for modbus RS485 comset
### `description`
`description` allows to define user friendly and language specific names via `de`, `en`, `generic`
### `dependencies`
`dependencies` allows to define a list of checks, when this param should be presented to the user, if it should be only in special cases
#### `name`
`name` referenced the `param` `name` value
#### `check`
`check` defines which kind of check should be performed
**Possible values**:
- `empty`: if the `value` of the referenced `param` `name` should be empty
- `notempty`: if the `value` of the referenced `param` `name` should be **NOT** empty
- `equal`: if the `value` of the referenced `param` `name` should match the value of the `value` property
#### `value`
`value` property is used in the `equal` `check`
### `required`
`required: true` defines if the user has to provide a value. Default is `false`

View file

@ -1,5 +1,6 @@
template: abl
description: ABL eMH / SENEC.Wallbox pro
description:
generic: ABL eMH / SENEC.Wallbox pro
requirements:
sponsorship: true
params:

View file

@ -1,5 +1,6 @@
template: cfos_charger
description: cFos PowerBrain
template: cfos
description:
generic: cFos PowerBrain
requirements:
sponsorship: true
params:

View file

@ -1,7 +1,6 @@
template: daheimladen
description: Daheimladen (Cloud API)
requirements:
sponsorship: true
description:
generic: Daheimladen (Cloud API)
params:
- name: token
help:

View file

@ -1,5 +1,6 @@
template: easee
description: Easee Home (Cloud API)
description:
generic: Easee Home (Cloud API)
requirements:
sponsorship: true
params:

View file

@ -1,6 +1,10 @@
template: eebus
description: EEBUS kompatible Wallbox
description:
de: EEBUS kompatible Wallbox
en: EEBUS compatible Wallbox
generic: true
capabilities:
iso151182: true
requirements:
eebus: true
params:

View file

@ -1,5 +1,6 @@
template: evse_din
description: EVSE DIN
description:
generic: EVSE DIN
params:
- name: modbus
choice: ["rs485"]

View file

@ -1,5 +1,6 @@
template: evsewifi
description: EVSE-Wifi
description:
generic: EVSE-Wifi
params:
- name: host
required: true

View file

@ -1,5 +1,6 @@
template: fritzdect
description: FritzDECT
description:
generic: FritzDECT
params:
- name: uri
default: https://fritz.box

View file

@ -1,5 +1,6 @@
template: go-e
description: go-eCharger Home/Pro/V3
description:
generic: go-eCharger Home/Pro/V3
requirements:
description:
en: Home und PRO require firmware 040.0 or later, V3 requires firmware 052.1 or later.

View file

@ -1,5 +1,6 @@
template: heidelberg
description: Heidelberg Energy Control (Modbus RTU)
description:
generic: Heidelberg Energy Control (Modbus RTU)
requirements:
sponsorship: true
params:

View file

@ -1,5 +1,6 @@
template: ichargecion
description: i-CHARGE CION (Modbus RTU-over-TCP)
description:
generic: i-CHARGE CION (Modbus RTU-over-TCP)
params:
- name: modbus
choice: ["tcpip"]

View file

@ -1,5 +1,6 @@
template: innogy-ebox
description: Innogy eBox
description:
generic: Innogy eBox
requirements:
sponsorship: true
params:

View file

@ -1,5 +1,6 @@
template: keba
description: KEBA Connect
description:
generic: KEBA Connect
requirements:
description:
en: This requires the UDP function to be enabled with DIP 1.3 = ON, see the installation manual.

View file

@ -1,5 +1,6 @@
template: nrgkick-bluetooth
description: NRGKick Bluetooth
description:
generic: NRGKick Bluetooth
params:
- name: mac
required: true

View file

@ -1,5 +1,6 @@
template: nrgkick-connect
description: NRGKick Connect
description:
generic: NRGKick Connect
params:
- name: host
required: true

View file

@ -1,5 +1,6 @@
template: openwb
description: openWB (MQTT)
description:
generic: openWB (MQTT)
requirements:
description:
en: The wallbox has to be configured running as a loadpoint.

View file

@ -1,5 +1,6 @@
template: phoenix-em-eth
description: Phoenix EM-CP-PP-ETH Controller (Modbus TCP)
description:
generic: Phoenix EM-CP-PP-ETH Controller (Modbus TCP)
generic: true
requirements:
description:

View file

@ -1,5 +1,6 @@
template: phoenix-ev-eth
description: Phoenix EV-ETH Controller (Modbus TCP)
description:
generic: Phoenix EV-ETH Controller (Modbus TCP)
generic: true
requirements:
description:

View file

@ -1,5 +1,6 @@
template: phoenix-ev-ser
description: Phoenix EV-SER Controller (Modbus RTU)
description:
generic: Phoenix EV-SER Controller (Modbus RTU)
generic: true
params:
- name: modbus

View file

@ -1,5 +1,8 @@
template: pmcc
description: Porsche Mobile Charger Connect
description:
generic: Porsche Mobile Charger Connect
capabilities:
iso151182: true
requirements:
eebus: true
params:

View file

@ -1,5 +1,6 @@
template: pmcp
description: Porsche Mobile Charger Plus
description:
generic: Porsche Mobile Charger Plus
requirements:
eebus: true
params:

View file

@ -1,5 +1,6 @@
template: shelly
description: Shelly
description:
generic: Shelly
params:
- name: host
required: true

View file

@ -1,5 +1,6 @@
template: tasmota
description: Tasmota
description:
generic: Tasmota
params:
- name: host
required: true

View file

@ -1,5 +1,6 @@
template: tinkerforge-warp-pro
description: TinkerForge WARP Charger Pro
description:
generic: TinkerForge WARP Charger Pro
requirements:
description:
en: A MQTT Broker software needs to be running on a computer. The MQTT connection also needs to be configured in the wallbox.

View file

@ -1,5 +1,6 @@
template: tinkerforge-warp
description: TinkerForge WARP Charger
description:
generic: TinkerForge WARP Charger
requirements:
description:
en: A MQTT Broker software needs to be running on a computer. The MQTT connection also needs to be configured in the wallbox.

View file

@ -1,5 +1,6 @@
template: tplink
description: TP-LINK Smart Plug
description:
generic: TP-LINK Smart Plug
params:
- name: host
required: true

View file

@ -1,5 +1,7 @@
template: wallbe-meter
description: Wallbe (Eco, Pro) with Meter
description:
de: Wallbe (Eco, Pro) mit Strommessgerät
en: Wallbe (Eco, Pro) with Meter
requirements:
description:
en: The Wallbe must be connected using Ethernet and the DIP switch 10 must be set to 'ON'.

View file

@ -1,5 +1,7 @@
template: wallbe-pre2019-meter
description: Wallbe (Eco, Pro) (pre ~2019) with Meter
description:
de: Wallbe (Eco, Pro) (vor ~2019) mit Strommessgerät
en: Wallbe (Eco, Pro) (pre ~2019) with Meter
requirements:
description:
en: The Wallbe must be connected using Ethernet and the DIP switch 10 must be set to 'ON'.

View file

@ -1,5 +1,7 @@
template: wallbe-pre2019
description: Wallbe (Eco, Pro) (pre ~2019)
description:
de: Wallbe (Eco, Pro) (vor ~2019)
en: Wallbe (Eco, Pro) (pre ~2019)
requirements:
description:
en: The Wallbe must be connected using Ethernet and the DIP switch 10 must be set to 'ON'.

View file

@ -1,5 +1,6 @@
template: wallbe
description: Wallbe (Eco, Pro)
description:
generic: Wallbe (Eco, Pro)
requirements:
description:
en: The Wallbe must be connected using Ethernet and the DIP switch 10 must be set to 'ON'.

View file

@ -1,5 +1,6 @@
template: cfos-meter
description: cFos PowerBrain Meter
template: cfos
description:
generic: cFos PowerBrain Meter
requirements:
sponsorship: true
params:

View file

@ -1,5 +1,6 @@
template: discovergy
description: Discovergy (Cloud)
description:
generic: Discovergy (Cloud)
params:
- name: usage
choice: ["grid", "pv"]

View file

@ -1,5 +1,6 @@
template: e3dc
description: E3DC
description:
generic: E3DC
guidedsetup:
enable: true
params:

View file

@ -1,5 +1,7 @@
template: eastron
description: Eastron SDM Modbus Meter
description:
de: Eastron SDM Modbus Strommessgeräte
en: Eastron SDM Modbus Meter
params:
- name: usage
choice: ["grid", "pv", "battery", "charge"]

View file

@ -1,5 +1,6 @@
template: fritzdect
description: FritzDECT
description:
generic: FritzDECT
params:
- name: usage
choice: ["pv", "charge"]
@ -17,12 +18,9 @@ params:
help:
en: The AIN is printed on the type label on the back of the device.
de: Die AIN ist auf dem Typenschild auf der Geräterückseite aufgedruckt.
- name: standbypower
default: 15
render: |
type: fritzdect
uri: {{ .uri }}
user: {{ .user }}
password: {{ .password }}
ain: {{ .ain }} # switch actor identification number without blanks (see AIN number on switch sticker)
standbypower: {{ .standbypower }} # treat as charging above this power

View file

@ -1,5 +1,6 @@
template: fronius-gen24
description: Fronius Symo GEN24 Plus
description:
generic: Fronius Symo GEN24 Plus
guidedsetup:
enable: true
params:

View file

@ -1,5 +1,6 @@
template: fronius-solarapi-v1
description: Fronius Solar API V1
description:
generic: Fronius Solar API V1
guidedsetup:
enable: true
params:

View file

@ -1,5 +1,6 @@
template: huawei-sun2000-8ktl
description: Huawei SUN2000-8KTL
description:
generic: Huawei SUN2000-8KTL
params:
- name: usage
choice: [ "pv" ]

View file

@ -1,5 +1,7 @@
template: kostal-ksem-inverter
description: Kostal Smart Energy Meter via inverter
description:
de: Kostal Smart Energy Meter über den Wechselrichter
en: Kostal Smart Energy Meter via inverter
params:
- name: usage
choice: [ "grid" ]

View file

@ -1,5 +1,6 @@
template: kostal-ksem
description: Kostal Smart Energy Meter
description:
generic: Kostal Smart Energy Meter
params:
- name: usage
choice: [ "grid" ]

View file

@ -1,5 +1,6 @@
template: kostal-piko-mp-plus
description: Kostal Piko MP Plus
description:
generic: Kostal Piko MP Plus
guidedsetup:
enable: true
linked:

View file

@ -1,5 +1,6 @@
template: kostal-piko
description: Kostal Piko + Piko BA
description:
generic: Kostal Piko + Piko BA
guidedsetup:
enable: true
params:
@ -17,18 +18,18 @@ render: |
add:
- source: http
uri: http://{{ .host }}/api/dxs.json?dxsEntries=67109120 # PV AC Ausgang
jq: .dxsEntries[0].value
jq: .dxsEntries[0].value | if . == null then 0 else . end
scale: -1.0
- source: http
uri: http://{{ .host }}/api/dxs.json?dxsEntries=83886848 # Netzbezug
jq: .dxsEntries[0].value
jq: .dxsEntries[0].value | if . == null then 0 else . end
- source: http
uri: http://{{ .host }}/api/dxs.json?dxsEntries=83886336 # PV Eigenverbrauch
jq: .dxsEntries[0].value
jq: .dxsEntries[0].value | if . == null then 0 else . end
{{- end -}}
{{- if eq .usage "pv" }}
# PV
source: http
uri: http://{{ .host }}/api/dxs.json?dxsEntries=67109120 # PV AC Ausgang
jq: .dxsEntries[0].value
jq: .dxsEntries[0].value | if . == null then 0 else . end
{{- end -}}

View file

@ -1,5 +1,6 @@
template: kostal-plenticore
description: Kostal Plenticore Hybrid
description:
generic: Kostal Plenticore Hybrid
guidedsetup:
enable: true
linked:

View file

@ -1,5 +1,6 @@
template: lg-ess-home-8-10
description: LG ESS Home 8/10
description:
generic: LG ESS Home 8/10
guidedsetup:
enable: true
params:

View file

@ -0,0 +1,105 @@
template: mqtt-meter
description:
de: MQTT Strommessgerät
en: MQTT Meter
generic: true
requirements:
mqtt: true
params:
- name: usage
choice: [ "grid", "pv", "charge", "battery" ]
- name: power
description:
de: MQTT Topic für die Leistung (W)
en: MQTT Topic for power (W)
required: true
- name: scalepower
description:
de: Skalierung der Leistungswerte
en: Scaling of the power value
default: 1
help:
de: Die Leistung wird in Watt erwartet. Falls z.b. kW geliefert wird, kann dies hier mit dem Wert 1000 angepasst werden
en: Power is expected to be provided in Watt. If it is provided in kW instead, set this value to 1000 to correct it
- name: energy
description:
de: MQTT Topic für Energiemenge (Wh)
en: MQTT Topic for energy (Wh)
- name: scaleenergy
dependencies:
- name: energy
check: notempty
description:
de: Skalierung der Energiement
en: Scaling of the energy value
default: 1
help:
de: Die Energiemenge wird in kWh erwartet. Falls z.b. Wh geliefert wird, kann dies hier mit dem Wert 0.001 angepasst werden
en: Power is expected to be provided in kWh. If it is provided in Wh instead, set this value to 0.001 to correct it
- name: soc
description:
de: MQTT Topic für den SoC der Batterie (%)
en: MQTT Topic for battery SoC (%)
dependencies:
- name: usage
check: equal
value: battery
- name: currentp1
description:
de: MQTT Topic Stromstärke (A) Phase 1
en: MQTT Topic Amperage (A) phase 1
- name: currentp2
description:
de: MQTT Topic Stromstärke (A) Phase 2
en: MQTT Topic Amperage (A) phase 2
dependencies:
- name: currentp1
check: notempty
- name: currentp3
description:
de: MQTT Topic Stromstärke (A) Phase 3
en: MQTT Topic Amperage (A) phase 3
dependencies:
- name: currentp1
check: notempty
- name: timeout
default: 30
help:
de: Akzeptiere keine Daten die älter als dieser Wert in Sekunden ist
en: Don't accept values older than this value in seconds
render: |
type: custom
power:
source: mqtt
topic: {{ .power }}
scale: {{ .scalepower }}
timeout: {{ .timeout }}
{{- if ne .energy1 "" }}
energy:
source: mqtt
topic: {{ .energy }}
scale: {{ .scaleenergy }}
timeout: {{ .timeout }}
{{- end -}}
{{- if eq .usage "battery" }}
soc:
source: mqtt
topic: {{ .soc }}
timeout: {{ .timeout }}
{{- end -}}
{{- if ne .currentp1 "" }}
currents:
- source: mqtt
topic: {{ .currentp1 }}
timeout: {{ .timeout }}
{{- if ne .currentp2 "" }}
- source: mqtt
topic: {{ .currentp2 }}
timeout: {{ .timeout }}
{{- end -}}
{{- if ne .currentp3 "" }}
- source: mqtt
topic: {{ .currentp3 }}
timeout: {{ .timeout }}
{{- end -}}
{{- end -}}

View file

@ -1,5 +1,6 @@
template: powerdog
description: Powerdog
description:
generic: Powerdog
guidedsetup:
enable: true
params:

View file

@ -1,5 +1,6 @@
template: powerfox-poweropti
description: Powerfox Poweropti (Cloud)
description:
generic: Powerfox Poweropti (Cloud)
params:
- name: usage
choice: [ "grid" ]

View file

@ -1,5 +1,6 @@
template: rct-power
description: RCT Power
description:
generic: RCT Power
guidedsetup:
enable: true
params:

View file

@ -1,5 +1,6 @@
template: senec-home
description: SENEC.Home
description:
generic: SENEC.Home
guidedsetup:
enable: true
params:

View file

@ -1,5 +1,6 @@
template: shelly-3em
description: Shelly 3EM
description:
generic: Shelly 3EM
params:
- name: usage
choice: [ "grid" ]

View file

@ -1,5 +1,6 @@
template: sma-energy-meter
description: SMA Energy Meter
description:
generic: SMA Energy Meter (Produktname)
params:
- name: usage
choice: ["grid", "pv", "battery"]

View file

@ -1,5 +1,6 @@
template: sma-home-manager
description: SMA Sunny Home Manager 2.0
description:
generic: SMA Sunny Home Manager 2.0
requirements:
hems: sma
guidedsetup:

View file

@ -1,5 +1,7 @@
template: sma-inverter
description: SMA Inverter/Wechselrichter
description:
de: SMA Wechselrichter
en: SMA Inverter
params:
- name: usage
choice: ["pv", "battery"]

View file

@ -1,7 +1,12 @@
template: solaredge-hybrid
description: Solaredge Hybrid
description:
generic: Solaredge Hybrid
guidedsetup:
enable: true
requirements:
description:
de: Nur ein System kann auf den Wechselrichter zugreifen!
en: Only one system may access the inverter!
params:
- name: usage
choice: [ "grid", "pv", "battery" ]

View file

@ -0,0 +1,17 @@
template: solaredge-inverter
description:
de: Solaredge Wechselrichter
en: Solaredge Inverter
guidedsetup:
enable: true
params:
- name: usage
choice: [ "pv" ]
- name: modbus
choice: [ "tcpip" ]
id: 1
port: 1502
render: |
type: modbus
model: sunspec
{{ include "modbus" . }}

View file

@ -1,5 +1,6 @@
template: solarlog
description: Solarlog
description:
generic: Solarlog
guidedsetup:
enable: true
params:

View file

@ -1,5 +1,6 @@
template: solarwatt-myreserve
description: Solarwatt MyReserve
description:
generic: Solarwatt MyReserve
guidedsetup:
enable: true
params:

View file

@ -1,5 +1,6 @@
template: sonnenbatterie-eco10
description: Sonnenbatterie Eco/10
description:
generic: Sonnenbatterie Eco/10
guidedsetup:
enable: true
params:

View file

@ -0,0 +1,39 @@
template: sungrow
description:
generic: Sungrow (Beta)
guidedsetup:
enable: true
params:
- name: usage
choice: [ "grid", "pv", "battery" ]
- name: modbus
choice: [ "rs485", "tcpip" ]
render: |
type: custom
power:
source: modbus
{{include "modbus" . | indent 2}}
register:
type: input
{{- if eq .usage "grid" }}
address: 13009 # Export power
decode: int32s
scale: -1
{{- end }}
{{- if eq .usage "pv" }}
address: 5016 # Total DC power
decode: uint32s
{{- end }}
{{- if eq .usage "battery" }}
address: 13021 # Battery power
decode: int16
#scale: -1 # charging sign form 13000 & 0x2
soc:
source: modbus
{{include "modbus" . | indent 2}}
register:
address: 13022 # Battery level
type: input
decode: int16
scale: 0.1
{{- end }}

View file

@ -1,5 +1,7 @@
template: sunspec
description: Inverter / Wechselrichter (SunSpec)
description:
de: Wechselrichter (SunSpec)
en: Inverter (SunSpec)
generic: true
params:
- name: usage

View file

@ -1,5 +1,6 @@
template: tesla-powerwall
description: Tesla Powerwall
description:
generic: Tesla Powerwall
guidedsetup:
enable: true
params:

View file

@ -1,5 +1,6 @@
template: varta-energiespeicher
description: VARTA Energiespeicher
description:
generic: VARTA Energiespeicher
guidedsetup:
enable: true
params:

View file

@ -0,0 +1,69 @@
template: victron-energy
description:
generic: Victron Energy
guidedsetup:
enable: true
params:
- name: usage
choice: [ "grid", "pv", "battery" ]
- name: host
required: true
example: 192.0.2.2
- name: port
default: 502
render: |
type: custom
power:
{{- if eq .usage "grid" }}
source: calc
add:
- source: modbus
uri: {{ .host }}:{{ .port }}
id: 50 # com.victronenergy.grid
register:
address: 2600 # L1 grid power
type: input
decode: int16
- source: modbus
uri: {{ .host }}:{{ .port }}
id: 50 # com.victronenergy.grid
register:
address: 2601 # L2 grid power
type: input
decode: int16
- source: modbus
uri: {{ .host }}:{{ .port }}
id: 50 # com.victronenergy.grid
register:
address: 2602 # L3 grid power
type: input
decode: int16
{{- end }}
{{- if eq .usage "pv" }}
source: modbus
uri: {{ .host }}:{{ .port }}
id: 20 # com.victronenergy.pvinverter
register:
address: 1052 # Total AC Power
type: input
decode: int32
{{- end }}
{{- if eq .usage "battery" }}
source: modbus
uri: {{ .host }}:{{ .port }}
id: 100 # com.victronenergy.system
register:
address: 842 # active DC power
type: input
decode: int16
scale: -1
soc:
source: modbus
uri: {{ .host }}:{{ .port }}
id: 225 # com.victronenergy.battery
register:
address: 266 # SoC
type: input
decode: uint16
scale: 0.1
{{- end }}

View file

@ -1,5 +1,6 @@
template: volkszaehler-http
description: Volkszähler (HTTP API)
description:
generic: Volkszähler (HTTP API)
generic: true
params:
- name: usage

View file

@ -1,5 +1,6 @@
template: volkszaehler-importexport
description: Volkszähler (HTTP API, Import & Export)
description:
generic: Volkszähler (HTTP API, Import & Export)
generic: true
params:
- name: usage

View file

@ -1,5 +1,6 @@
template: volkszaehler-ws
description: Volkszähler (WebSocket API)
description:
generic: Volkszähler (WebSocket API)
generic: true
params:
- name: usage

View file

@ -1,5 +1,6 @@
template: vzlogger
description: vzlogger
description:
generic: vzlogger
generic: true
params:
- name: usage

View file

@ -1,5 +1,6 @@
template: audi
description: Audi
description:
generic: Audi
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: bmw
description: BMW
description:
generic: BMW
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: carwings
description: Nissan Leaf (pre 2019)
description:
generic: Nissan Leaf (pre 2019)
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: citroen
description: Citroen
description:
generic: Citroen
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: dacia
description: Dacia
description:
generic: Dacia
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: ds
description: DS
description:
generic: DS
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,7 @@
template: evnotify
description: evNotify
description:
generic: evNotify
generic: true
params:
- name: title
- name: akey

View file

@ -1,5 +1,6 @@
template: fiat
description: Fiat
description:
generic: Fiat
params:
- base: vehiclebase
- name: vin

View file

@ -1,5 +1,6 @@
template: ford
description: Ford
description:
generic: Ford
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: hyundai
description: Hyundai
description:
generic: Hyundai
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: kia
description: Kia
description:
generic: Kia
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: mini
description: Mini
description:
generic: Mini
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: nissan
description: Nissan
description:
generic: Nissan
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: niu-e-scooter
description: NIU E-Scooter
description:
generic: NIU E-Scooter
params:
- name: title
- name: user

View file

@ -1,5 +1,6 @@
template: opel
description: Opel
description:
generic: Opel
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,7 @@
template: ovms
description: Open Vehicle Monitoring System
description:
generic: Open Vehicle Monitoring System
generic: true
params:
- name: title
- name: user

View file

@ -1,5 +1,6 @@
template: peugeot
description: Peugeot
description:
generic: Peugeot
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: porsche
description: Porsche
description:
generic: Porsche
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: renault
description: Renault
description:
generic: Renault
params:
- base: vehiclebase
- base: vehicleidentify

View file

@ -1,5 +1,6 @@
template: seat
description: Seat (Cupra, Mii)
description:
generic: Seat (Cupra, Mii)
params:
- base: vehiclebase
- base: vehicleidentify

Some files were not shown because too many files have changed in this diff Show more