Template enhancements (#2274)

- Only add SMA HEMS Setup, if required values exist (e.g. machine-id). Also asks the user if this should be added
- New templates: Alfen, PC Electric Garo, go-e V3 (moved into separate template), Tasmota meter, Huawei inverter with Sdongle
- Updated templates: SolarEdge Inverter, Sunspec
- New capabilities can be assigned: 1p3p, RFID
- Added group information instead of `generic` flag. Used e.g. for marking power plugs or generic devices
- Requirement description text can now be markdown text (required for documentation page)
- Replaced `description` with product list, separating brand from description (required for documentation page)
- Generate newly structured documentation yaml files
- Fix https://github.com/evcc-io/evcc/issues/2385
This commit is contained in:
Andreas Linde 2022-01-23 15:21:13 +01:00 • committed by GitHub
parent ba662fb9b7
commit a52f52b406
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
388 changed files with 4442 additions and 1808 deletions

1
.gitignore vendored
View file

@ -6,6 +6,7 @@ __debug_bin
*.json
*.yaml
!templates/**/*.yaml
!util/templates/**/*.yaml
!package*.json
!evcc.dist.yaml
evcc

View file

@ -26,6 +26,7 @@ var acceptable = []string{
"sponsorship required, see https://github.com/evcc-io/evcc#sponsorship",
"eebus not configured",
"unexpected status: 400", // easee
"Get \"http://192.0.2.2/getParameters\": context deadline exceeded", // evsewifi
}
func TestChargerTemplates(t *testing.T) {
@ -45,9 +46,9 @@ func TestChargerTemplates(t *testing.T) {
modbusChoices := tmpl.ModbusChoices()
// we only test one modbus setup
if funk.ContainsString(modbusChoices, templates.ModbusChoiceTCPIP) {
values[templates.ModbusTCPIP] = true
values[templates.ModbusKeyTCPIP] = true
} else {
values[templates.ModbusRS485TCPIP] = true
values[templates.ModbusKeyRS485TCPIP] = true
}
}

View file

@ -30,8 +30,7 @@ loadpoints:
- title: {{ .Title }}
charger: {{ .Charger }}
{{- if .ChargeMeter }}
meters:
charge: {{ .ChargeMeter }}
meter: {{ .ChargeMeter }}
{{- end }}
{{- if ne (len .Vehicles) 0 }}
vehicles:

View file

@ -101,9 +101,16 @@ func (d *DeviceTest) testCharger(v interface{}) (DeviceTestResult, error) {
// testMeter tests a meter device
func (d *DeviceTest) testMeter(deviceCategory DeviceCategory, v interface{}) (DeviceTestResult, error) {
if v, ok := v.(api.Meter); ok {
if _, err := v.CurrentPower(); err != nil {
power, err := v.CurrentPower()
if err != nil {
return DeviceTestResultInvalid, err
}
// check if the grid meter reports power 0, which should be impossible
// happens with Kostal Piko charger that do not have a grid meter attached
// but we can't determine this
if power == 0 && deviceCategory == DeviceCategoryGridMeter {
return DeviceTestResultInvalid, errors.New("grid meter reports power 0")
}
if deviceCategory == DeviceCategoryBatteryMeter {
if v, ok := v.(api.Battery); ok {

View file

@ -72,6 +72,7 @@ func (c *CmdConfigure) configureDeviceGuidedSetup() {
}
c.configuration.AddDevice(deviceItem, deviceCategory)
c.processDeviceCapabilities(templateItem.Capabilities)
if len(supportedDeviceCategories) > 1 {
for _, additionalCategory := range supportedDeviceCategories[1:] {
@ -86,7 +87,7 @@ func (c *CmdConfigure) configureDeviceGuidedSetup() {
}
fmt.Println()
fmt.Println(templateItem.Description.String(c.lang) + " " + c.localizedString("Device_Added", nil))
fmt.Println(templateItem.Title() + " " + c.localizedString("Device_Added", nil))
c.configureLinkedTypes(templateItem)
}
@ -118,11 +119,12 @@ func (c *CmdConfigure) configureLinkedTypes(templateItem templates.Template) {
if len(linkedTemplateItem.Params) == 0 || linkedTemplate.Usage == "" {
break
}
linkedTemplateItem.SetCombinedTitle()
category := DeviceCategory(linkedTemplate.Usage)
localizeMap := localizeMap{
"Linked": linkedTemplateItem.Description.String(c.lang),
"Linked": linkedTemplateItem.Title(),
"Article": DeviceCategories[category].article,
"Additional": DeviceCategories[category].additional,
"Category": DeviceCategories[category].title,
@ -152,12 +154,12 @@ func (c *CmdConfigure) configureLinkedTypes(templateItem templates.Template) {
// 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 {
func (c *CmdConfigure) configureLinkedTemplate(templateItem templates.Template, category DeviceCategory) bool {
for ok := true; ok; {
deviceItem := device{}
values := c.processConfig(template, category)
deviceItem, err := c.processDeviceValues(values, template, deviceItem, category)
values := c.processConfig(templateItem, category)
deviceItem, err := c.processDeviceValues(values, templateItem, deviceItem, category)
if err != nil {
if !errors.Is(err, c.errDeviceNotValid) {
fmt.Println()
@ -170,9 +172,10 @@ func (c *CmdConfigure) configureLinkedTemplate(template templates.Template, cate
} else {
c.configuration.AddDevice(deviceItem, category)
c.processDeviceCapabilities(templateItem.Capabilities)
fmt.Println()
fmt.Println(template.Description.String(c.lang) + " " + c.localizedString("Device_Added", nil))
fmt.Println(templateItem.Title() + " " + c.localizedString("Device_Added", nil))
return true
}
break
@ -181,7 +184,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, templates.Capabilities, error) {
func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (device, []string, error) {
fmt.Println()
fmt.Printf("- %s %s\n", c.localizedString("Device_Configure", nil), DeviceCategories[deviceCategory].title)
@ -190,7 +193,7 @@ func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (d
}
var deviceDescription string
var capabilities templates.Capabilities
var capabilities []string
// repeat until the device is added or the user chooses to continue without adding a device
for ok := true; ok; {
@ -201,7 +204,7 @@ func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (d
return device, capabilities, c.errItemNotPresent
}
deviceDescription = templateItem.Description.String(c.lang)
deviceDescription = templateItem.Title()
capabilities = templateItem.Capabilities
values := c.processConfig(templateItem, deviceCategory)
@ -223,6 +226,7 @@ func (c *CmdConfigure) configureDeviceCategory(deviceCategory DeviceCategory) (d
}
c.configuration.AddDevice(device, deviceCategory)
c.processDeviceCapabilities(capabilities)
var deviceTitle string
if device.Title != "" {

View file

@ -10,6 +10,7 @@ import (
"github.com/evcc-io/evcc/util/sponsor"
"github.com/evcc-io/evcc/util/templates"
"github.com/thoas/go-funk"
stripmd "github.com/writeas/go-strip-markdown"
"gopkg.in/yaml.v3"
)
@ -17,7 +18,7 @@ import (
func (c *CmdConfigure) processDeviceSelection(deviceCategory DeviceCategory) (templates.Template, error) {
templateItem := c.selectItem(deviceCategory)
if templateItem.Description.String(c.lang) == c.localizedString("ItemNotPresent", nil) {
if templateItem.Title() == c.localizedString("ItemNotPresent", nil) {
return templateItem, c.errItemNotPresent
}
@ -33,7 +34,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.String(c.lang)
device.Title = templateItem.Title()
for item, value := range values {
if strings.ToLower(item) != "title" {
continue
@ -47,9 +48,9 @@ func (c *CmdConfigure) processDeviceValues(values map[string]interface{}, templa
fmt.Println()
if categoryWithUsage {
fmt.Println(c.localizedString("TestingDevice_TitleUsage", localizeMap{"Device": templateItem.Description.String(c.lang), "Usage": deviceCategory.String()}))
fmt.Println(c.localizedString("TestingDevice_TitleUsage", localizeMap{"Device": templateItem.Title(), "Usage": deviceCategory.String()}))
} else {
fmt.Println(c.localizedString("TestingDevice_Title", localizeMap{"Device": templateItem.Description.String(c.lang)}))
fmt.Println(c.localizedString("TestingDevice_Title", localizeMap{"Device": templateItem.Title()}))
}
deviceTest := DeviceTest{
@ -63,9 +64,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.String(c.lang)})
question := c.localizedString("TestingDevice_AddFailed", localizeMap{"Device": templateItem.Title()})
if categoryWithUsage {
question = c.localizedString("TestingDevice_AddFailedUsage", localizeMap{"Device": templateItem.Description.String(c.lang), "Usage": deviceCategory.String()})
question = c.localizedString("TestingDevice_AddFailedUsage", localizeMap{"Device": templateItem.Title(), "Usage": deviceCategory.String()})
}
if !c.askYesNo(question) {
c.addedDeviceIndex--
@ -79,7 +80,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, c.lang, false)
b, err := templateItem.RenderProxyWithValues(values, c.lang)
if err != nil {
c.addedDeviceIndex--
return device, err
@ -105,33 +106,35 @@ func (c *CmdConfigure) processDeviceValues(values map[string]interface{}, templa
return device, nil
}
func (c *CmdConfigure) processDeviceCapabilities(capabilitites []string) {
if funk.ContainsString(capabilitites, templates.CapabilitySMAHems) {
c.capabilitySMAHems = true
}
}
// processDeviceRequirements handles device requirements
func (c *CmdConfigure) processDeviceRequirements(templateItem templates.Template) error {
if len(templateItem.Requirements.Description.String(c.lang)) > 0 {
requirementDescription := stripmd.Strip(templateItem.Requirements.Description.String(c.lang))
if len(requirementDescription) > 0 {
fmt.Println()
fmt.Println("-------------------------------------------------")
fmt.Println(c.localizedString("Requirements_Title", nil))
fmt.Println(" ", templateItem.Requirements.Description.String(c.lang))
fmt.Println(requirementDescription)
if len(templateItem.Requirements.URI) > 0 {
fmt.Println(" " + c.localizedString("Requirements_More", nil) + " " + templateItem.Requirements.URI)
}
fmt.Println("-------------------------------------------------")
}
// check if sponsorship is required
if templateItem.Requirements.Sponsorship && c.configuration.config.SponsorToken == "" {
if funk.ContainsString(templateItem.Requirements.EVCC, templates.RequirementSponsorship) && c.configuration.config.SponsorToken == "" {
if err := c.askSponsortoken(true); err != nil {
return err
}
}
// check if we need to setup a HEMS
if templateItem.Requirements.Hems != "" && funk.ContainsString(templates.HemsValueTypes, templateItem.Requirements.Hems) {
switch templateItem.Requirements.Hems {
case templates.HemsTypeSMA:
c.configuration.config.Hems = "type: sma\nAllowControl: false\n"
}
}
// check if we need to setup an MQTT broker
if templateItem.Requirements.Mqtt {
if funk.ContainsString(templateItem.Requirements.EVCC, templates.RequirementMQTT) {
if c.configuration.config.MQTT == "" {
mqttConfig, err := c.configureMQTT()
if err != nil {
@ -148,7 +151,7 @@ func (c *CmdConfigure) processDeviceRequirements(templateItem templates.Template
}
// check if we need to setup an EEBUS HEMS
if templateItem.Requirements.Eebus {
if funk.ContainsString(templateItem.Requirements.EVCC, templates.RequirementEEBUS) {
if c.configuration.config.EEBUS == "" {
fmt.Println()
fmt.Println("-- EEBUS -----------------------------------")
@ -185,6 +188,9 @@ func (c *CmdConfigure) askSponsortoken(required bool) error {
if required {
fmt.Println()
fmt.Println(c.localizedString("Requirements_Sponsorship_Title", nil))
} else {
fmt.Println()
fmt.Println(c.localizedString("Requirements_Sponsorship_Optional_Title", nil))
}
fmt.Println()
if !c.askYesNo(c.localizedString("Requirements_Sponsorship_Token", nil)) {
@ -261,33 +267,49 @@ func (c *CmdConfigure) configureMQTT() (map[string]interface{}, error) {
// 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.String(c.lang)) == 0 {
if len(tmpl.Params) == 0 {
continue
}
if deviceCategory == DeviceCategoryGuidedSetup {
if tmpl.GuidedSetup.Enable {
items = append(items, tmpl)
for _, t := range tmpl.Titles(c.lang) {
titleTmpl := templates.Template{
TemplateDefinition: tmpl.TemplateDefinition,
ConfigDefaults: tmpl.ConfigDefaults,
Lang: c.lang,
}
} else {
if len(DeviceCategories[deviceCategory].categoryFilter) == 0 ||
c.paramChoiceContains(tmpl.Params, templates.ParamUsage, DeviceCategories[deviceCategory].categoryFilter.String()) {
items = append(items, tmpl)
title := t
groupTitle := titleTmpl.GroupTitle()
if groupTitle != "" {
title += " [" + groupTitle + "]"
}
titleTmpl.SetTitle(title)
if deviceCategory == DeviceCategoryGuidedSetup {
if tmpl.GuidedSetup.Enable {
items = append(items, titleTmpl)
}
} else {
if len(DeviceCategories[deviceCategory].categoryFilter) == 0 ||
c.paramChoiceContains(tmpl.Params, templates.ParamUsage, DeviceCategories[deviceCategory].categoryFilter.String()) {
items = append(items, titleTmpl)
}
}
}
}
sort.Slice(items[:], func(i, j int) bool {
// sort generic templates to the bottom
if items[i].Generic && !items[j].Generic {
if items[i].Group != "" && items[j].Group == "" {
return false
}
if !items[i].Generic && items[j].Generic {
if items[i].Group == "" && items[j].Group != "" {
return true
}
return strings.ToLower(items[i].Description.String(c.lang)) < strings.ToLower(items[j].Description.String(c.lang))
if items[i].Group != items[j].Group {
return strings.ToLower(items[i].GroupTitle()) < strings.ToLower(items[j].GroupTitle())
}
return strings.ToLower(items[i].Title()) < strings.ToLower(items[j].Title())
})
return items
@ -319,31 +341,50 @@ func (c *CmdConfigure) paramChoiceValues(params []templates.Param, name string)
// Returns:
// a map with param name and values
func (c *CmdConfigure) processConfig(templateItem templates.Template, deviceCategory DeviceCategory) map[string]interface{} {
usageFilter := DeviceCategories[deviceCategory].categoryFilter
additionalConfig := make(map[string]interface{})
fmt.Println()
fmt.Println(c.localizedString("Config_Title", nil))
fmt.Println()
for _, param := range templateItem.Params {
c.processModbusConfig(&templateItem, deviceCategory)
return c.processParams(templateItem, templateItem.Params, deviceCategory)
}
// process a list of params
func (c *CmdConfigure) processParams(templateItem templates.Template, params []templates.Param, deviceCategory DeviceCategory) map[string]interface{} {
usageFilter := DeviceCategories[deviceCategory].categoryFilter
additionalConfig := make(map[string]interface{})
for _, param := range params {
if param.Dependencies != nil {
valid := true
for _, dep := range param.Dependencies {
_, valueParam := templateItem.ParamByName(dep.Name)
if valueParam == nil {
i, valueParam := templateItem.ParamByName(dep.Name)
if i == -1 {
break
}
value := valueParam.Value
switch dep.Check {
case templates.DependencyCheckEmpty:
valid = value == ""
if additionalConfig[dep.Name] != nil {
valid = additionalConfig[dep.Name] == ""
} else {
valid = value == ""
}
case templates.DependencyCheckNotEmpty:
valid = value != ""
if additionalConfig[dep.Name] != nil {
valid = additionalConfig[dep.Name] != ""
} else {
valid = value != ""
}
case templates.DependencyCheckEqual:
valid = value == dep.Value
if additionalConfig[dep.Name] != nil {
valid = additionalConfig[dep.Name] == dep.Value
} else {
valid = value == dep.Value
}
}
if !valid {
break
@ -356,7 +397,8 @@ func (c *CmdConfigure) processConfig(templateItem templates.Template, deviceCate
switch param.Name {
case templates.ParamModbus:
c.processModbusConfig(param, deviceCategory, additionalConfig)
additionalConfig[param.Name] = param.Value
case templates.ParamUsage:
if usageFilter != "" {
additionalConfig[param.Name] = usageFilter.String()
@ -366,6 +408,11 @@ func (c *CmdConfigure) processConfig(templateItem templates.Template, deviceCate
continue
}
if param.Hidden && param.Default != "" {
additionalConfig[param.Name] = param.Default
continue
}
switch param.ValueType {
case templates.ParamValueTypeStringList:
additionalConfig[param.Name] = c.processListInputConfig(param)
@ -401,9 +448,7 @@ 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
label := param.Name
langLabel := param.Description.String(c.lang)
if langLabel != "" {
label = langLabel
@ -411,110 +456,52 @@ func (c *CmdConfigure) processInputConfig(param templates.Param) string {
return c.askValue(question{
label: label,
defaultValue: userFriendly.Default,
exampleValue: userFriendly.Example,
help: userFriendly.Help.String(c.lang),
valueType: userFriendly.ValueType,
mask: userFriendly.Mask,
required: userFriendly.Required})
defaultValue: param.Default,
exampleValue: param.Example,
help: param.Help.String(c.lang),
valueType: param.ValueType,
mask: param.Mask,
required: param.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
deviceDefaultComset := templates.ModbusParamValueComset
deviceDefaultPort := templates.ModbusParamValuePort
deviceDefaultId := templates.ModbusParamValueId
if param.Baudrate != 0 {
deviceDefaultBaudrate = param.Baudrate
}
if param.Comset != "" {
deviceDefaultComset = param.Comset
}
if param.Port != 0 {
deviceDefaultPort = param.Port
}
if param.ID != 0 {
deviceDefaultId = param.ID
}
func (c *CmdConfigure) processModbusConfig(templateItem *templates.Template, deviceCategory DeviceCategory) {
var choices []string
var choiceKeys []string
var choiceTypes []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)
modbusIndex, modbusParam := templateItem.ParamByName(templates.ParamModbus)
if modbusIndex == -1 {
return
}
config := templateItem.ConfigDefaults.Config.Modbus
for _, choice := range modbusParam.Choice {
if config.Interfaces[choice] == nil {
continue
}
for _, itype := range config.Interfaces[choice] {
title := config.Types[itype].Description
choices = append(choices, title.String(c.lang))
choiceTypes = append(choiceTypes, itype)
}
}
if len(choices) > 0 {
// ask for modbus address
id := c.askValue(question{
label: "ID",
help: "Modbus ID",
defaultValue: deviceDefaultId,
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]
additionalConfig[templates.ParamModbus] = selectedModbusKey
switch selectedModbusKey {
case templates.ModbusKeyRS485Serial:
device := c.askValue(question{
label: c.localizedString("UserFriendly_Device_Name", nil),
help: c.localizedString("UserFriendly_Device_Help", nil),
exampleValue: templates.ModbusParamValueDevice,
required: true})
additionalConfig[templates.ModbusParamNameDevice] = device
baudrate := c.askValue(question{
label: c.localizedString("UserFriendly_Baudrate_Name", nil),
help: c.localizedString("UserFriendly_Baudrate_Help", nil),
defaultValue: deviceDefaultBaudrate,
valueType: templates.ParamValueTypeNumber,
required: true})
additionalConfig[templates.ModbusParamNameBaudrate] = baudrate
comset := c.askValue(question{
label: c.localizedString("UserFriendly_ComSet_Name", nil),
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: c.localizedString("UserFriendly_Host_Name", nil),
exampleValue: templates.ModbusParamValueHost,
required: true})
additionalConfig[templates.ModbusParamNameHost] = host
port := c.askValue(question{
label: c.localizedString("UserFriendly_Port_Name", nil),
defaultValue: deviceDefaultPort,
valueType: templates.ParamValueTypeNumber,
required: true})
additionalConfig[templates.ModbusParamNamePort] = port
}
if len(choices) == 0 {
return
}
// ask for modbus interface type
var index int
if len(choices) > 1 {
index, _ = c.askChoice(c.localizedString("Config_ModbusInterface", nil), choices)
}
values := make(map[string]interface{})
templateItem.Params[modbusIndex].Value = choiceTypes[index]
// add the interface type specific modbus params
templateItem.ModbusParams(choiceTypes[index], values)
// Update the modbus default values
templateItem.ModbusValues(templates.TemplateRenderModeInstance, values)
}

View file

@ -15,6 +15,9 @@ Flow_SingleDevice_Setup = "- Ein Gerät konfigurieren"
Flow_SingleDevice_Select = "Wähle eines der folgenden Gerätekategorien aus"
Flow_SingleDevice_Config = "Die Konfiguration lautet:"
Flow_SMAHems_Setup = "- SMA HEMS konfigurieren"
Flow_SMAHems_Add = "Möchtest du die Wallboxen an den SMA Home Manager anbinden, damit diese z.B. für die Steuerung der Hausbatterie berücksichtigt werden können?"
ItemNotPresent = "Mein Gerät ist nicht in der Liste"
AddDeviceInCategory = "Möchtest du {{ .Article }} {{ .Category }} hinzufügen?"
@ -37,35 +40,6 @@ File_SaveSuccess = "Die Konfiguration wurde erfolgreich in der Datei {{ .FileNam
Choose = "Wähle"
UserFriendly_Title_Name = "Titel"
UserFriendly_Title_Help = "Wird in der Benutzeroberfläche angezeigt"
UserFriendly_Device_Name = "Geräteadresse"
UserFriendly_Device_Help = "USB-RS485 Adapter Adresse"
UserFriendly_Baudrate_Name = "Baudrate"
UserFriendly_Baudrate_Help = "Prüfe die Geräteeinstellungen, typische Werte sind 9600, 19200, 38400, 57600, 115200"
UserFriendly_ComSet_Name = "ComSet"
UserFriendly_Host_Name = "IP-Adresse oder Hostname"
UserFriendly_Port_Name = "Port"
UserFriendly_User_Name = "Benutzername"
UserFriendly_Password_Name = "Passwort"
UserFriendly_Capacity_Name = "Akku-Kapazität in kWh"
UserFriendly_Vin_Name = "Fahrzeugidentifikationsnummer"
UserFriendly_Vin_Help = "Erforderlich, wenn mehrere Fahrzeuge des Herstellers vorhanden sind"
UserFriendly_Cache_Name = "Cache"
UserFriendly_Cache_Help = "Das zeitliche Interval, in welchem Daten vom Fahrzeug neu geladen werden soll"
UserFriendly_MinSoC_Name = "Minimaler Ladestand (SoC) in %"
UserFriendly_MinSoC_Help = "Lade sofort mit maximaler Geschwindigkeit bis zu dem angegeben Ladestand, wenn der Lademodus nicht auf 'Aus' steht"
UserFriendly_TargetSoC_Name = "Ziel-Ladestand (SoC) in %"
UserFriendly_TargetSoC_Help = "Bis zu welchem Ladestand (SoC) soll das Fahrzeug geladen werden"
UserFriendly_MinCurrent_Name = "Minimale Stromstärke in Ampere (A)"
UserFriendly_MinCurrent_Help = "Definiert die minimale Stromstärke pro angeschlossener Phase mit welcher das Fahrzeug geladen werden soll"
UserFriendly_MaxCurrent_Name = "Maximale Stromstärke in Ampere (A)"
UserFriendly_MaxCurrent_Help = "Definiert die maximale Stromstärke pro angeschlossener Phase mit welcher das Fahrzeug geladen werden soll"
UserFriendly_Identifier_Name = "Identifikationsnummer"
UserFriendly_Identifier_Help = "Kann meist erst später eingetragen werden, siehe: https://docs.evcc.io/docs/guides/vehicles/#erkennung-des-fahrzeugs-an-der-wallbox"
UserFriendly_StandByPower_Name = "Standby-Leistung in W"
UserFriendly_StandByPower_Help = "Leistung oberhalb des angegebenen Wertes wird als Ladeleistung gewertet"
Category_ChargerTitle = "Wallbox"
Category_ChargerArticle = "eine"
Category_ChargerAdditional = "eine weitere"
@ -96,10 +70,11 @@ TestingDevice_AddFailedUsage = "Der Test der {{ .Usage }} Konfiguration von {{ .
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_Title = "Das Gerät hat die folgenden Voraussetzungen:"
Requirements_More = "Weitere Informationen:"
Requirements_Sponsorship_Title = "Dieses Gerät benötigt ein Sponsorship von evcc. Wie das funktioniert und was ist, findest du hier: https://docs.evcc.io/docs/sponsorship"
Requirements_Sponsorship_Optional_Title = "Die Entwicklung von evcc kann über Sponsoring unterstützt werden. Wie das funktioniert und was ist, findest du hier: https://docs.evcc.io/docs/sponsorship"
Requirements_Sponsorship_Token = "Bist du ein Sponsor?"
Requirements_Sponsorship_Token_Input = "Bitte gib das Sponsortoken ein"

View file

@ -15,6 +15,9 @@ Flow_SingleDevice_Setup = "- Setup a device"
Flow_SingleDevice_Select = "Choose one of the following device categories"
Flow_SingleDevice_Config = "The configuration:"
Flow_SMAHems_Setup = "- Setup SMA HEMS"
Flow_SMAHems_Add = "Do you want to report your wallboxes to the SMA Home Manager anbinden, so that it can consider them e.g. for controlling the in-house battery?"
ItemNotPresent = "My device is not in this list"
AddDeviceInCategory = "Do you want to add {{ .Article }} {{ .Category }}?"
@ -37,35 +40,6 @@ File_SaveSuccess = "The configuration was successfully saved in the file {{ .Fil
Choose = "Choose"
UserFriendly_Title_Name = "Title"
UserFriendly_Title_Help = "Will be displayed in the user interface"
UserFriendly_Device_Name = "Device address"
UserFriendly_Device_Help = "USB-RS485 Adapter address"
UserFriendly_Baudrate_Name = "Baudrate"
UserFriendly_Baudrate_Help = "Check the device settings, typical values are 9600, 19200, 38400, 57600, 115200"
UserFriendly_ComSet_Name = "ComSet"
UserFriendly_Host_Name = "IP-Address or Hostname"
UserFriendly_Port_Name = "Port"
UserFriendly_User_Name = "Username"
UserFriendly_Password_Name = "Password"
UserFriendly_Capacity_Name = "Battery-Capacity in kWh"
UserFriendly_Vin_Name = "Vehicle Identification Number"
UserFriendly_Vin_Help = "Required if you own multiple vehicles of the same brand"
UserFriendly_Cache_Name = "Cache"
UserFriendly_Cache_Help = "Time interval with when data should be reloaded from the vehicle"
UserFriendly_MinSoC_Name = "Minimum state of charge (SoC) in %"
UserFriendly_MinSoC_Help = "Charge immediately with maximum power up to the defined state of charge, if the charge mode is not set to 'OFF'"
UserFriendly_TargetSoC_Name = "Target state of charge (SoC) in %"
UserFriendly_TargetSoC_Help = "Until which sate of charge (SoC) should the vehicle be charged"
UserFriendly_MinCurrent_Name = "Minimum amperage (A)"
UserFriendly_MinCurrent_Help = "The minimum amperage per connected phase with which the car should be charged"
UserFriendly_MaxCurrent_Name = "Maximum amperage (A)"
UserFriendly_MaxCurrent_Help = "The maximum amperage per connected phase with which the car shuold be charged"
UserFriendly_Identifier_Name = "Identification"
UserFriendly_Identifier_Help = "Mostly this can be added later, see: https://docs.evcc.io/docs/guides/vehicles/#erkennung-des-fahrzeugs-an-der-wallbox"
UserFriendly_StandByPower_Name = "Standby power in W"
UserFriendly_StandByPower_Help = "Power values above this value will be considered as charging power"
Category_ChargerTitle = "wallbox"
Category_ChargerArticle = "a"
Category_ChargerAdditional = "one more"
@ -96,10 +70,11 @@ TestingDevice_AddFailedUsage = "Testing of the {{ .Usage }} configuration of {{
TestingMQTTFailed = "Testing the MQTT configuration failed. Do you want to repeat its configuration?"
Requirements_Title = "The device needs to provide the following requirements:"
Requirements_Title = "The device has the following requirements:"
Requirements_More = "Additional information:"
Requirements_Sponsorship_Title = "This device requires an evcc sponsorship. Check the following link for what this is and how it works: https://docs.evcc.io/docs/sponsorship"
Requirements_Sponsorship_Optional_Title = "The development of evcc can be supported via sponsoring. Check the following link for what this is and how it works: https://docs.evcc.io/docs/sponsorship"
Requirements_Sponsorship_Token = "Are you already a sponsor?"
Requirements_Sponsorship_Token_Input = "Please enter the sponsortoken"

View file

@ -11,10 +11,12 @@ import (
"github.com/BurntSushi/toml"
"github.com/cloudfoundry/jibber_jabber"
"github.com/evcc-io/evcc/hems/semp"
"github.com/evcc-io/evcc/server"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/templates"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/thoas/go-funk"
"golang.org/x/text/language"
)
@ -33,6 +35,8 @@ type CmdConfigure struct {
advancedMode, expandedMode bool
addedDeviceIndex int
errItemNotPresent, errDeviceNotValid error
capabilitySMAHems bool
}
// Run starts the interactive configuration
@ -152,11 +156,17 @@ func (c *CmdConfigure) flowNewConfigFile() {
_ = c.configureDevices(DeviceCategoryPVMeter, true, true)
_ = c.configureDevices(DeviceCategoryBatteryMeter, true, true)
_ = c.configureDevices(DeviceCategoryVehicle, true, true)
c.configureLoadpoints()
c.configureSite()
// check if SMA HEMS is available and ask the user if it should be added
if c.capabilitySMAHems {
c.configureSMAHems()
}
if c.advancedMode && c.configuration.config.SponsorToken == "" {
_ = c.askSponsortoken(true)
_ = c.askSponsortoken(false)
}
yaml, err := c.configuration.RenderConfiguration()
@ -223,12 +233,14 @@ func (c *CmdConfigure) configureDevices(deviceCategory DeviceCategory, askAdding
}
for ok := true; ok; {
device, _, err := c.configureDeviceCategory(deviceCategory)
device, capabilities, err := c.configureDeviceCategory(deviceCategory)
if err != nil {
break
}
devices = append(devices, device)
c.processDeviceCapabilities(capabilities)
if !askMultiple {
break
}
@ -242,6 +254,25 @@ func (c *CmdConfigure) configureDevices(deviceCategory DeviceCategory, askAdding
return devices
}
// configureSMAHems asks the user if he wants to add the SMA HEMS
func (c *CmdConfigure) configureSMAHems() {
// check if the system provides a machine-id
if _, err := semp.UniqueDeviceID(); err != nil {
return
}
fmt.Println()
fmt.Println(c.localizedString("Flow_SMAHems_Setup", nil))
fmt.Println()
if !c.askYesNo(c.localizedString("Flow_SMAHems_Add", nil)) {
return
}
// check if we need to setup a HEMS
c.configuration.config.Hems = "type: sma\nAllowControl: false\n"
}
// configureLoadpoints asks loadpoint specific questions
func (c *CmdConfigure) configureLoadpoints() {
fmt.Println()
@ -289,7 +320,7 @@ func (c *CmdConfigure) configureLoadpoints() {
}
var minValue int = 6
if capabilities.ISO151182 {
if funk.ContainsString(capabilities, templates.CapabilityISO151182) {
minValue = 2
}

View file

@ -63,16 +63,16 @@ 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.SetString(c.localizedString("ItemNotPresent", nil), c.lang)
emptyItem := templates.Template{Lang: c.lang}
emptyItem.SetTitle(c.localizedString("ItemNotPresent", nil))
elements := c.fetchElements(deviceCategory)
elements = append(elements, emptyItem)
var items []string
for _, item := range elements {
if item.Description.String(c.lang) != "" {
items = append(items, item.Description.String(c.lang))
if item.Title() != "" {
items = append(items, item.Title())
}
}

View file

@ -2,11 +2,8 @@ package configure
import (
"errors"
"strings"
"github.com/evcc-io/evcc/util/templates"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/thoas/go-funk"
)
// setDefaultTexts sets default language specific texts
@ -39,59 +36,3 @@ func (c *CmdConfigure) localizedString(key string, templateData localizeMap) str
TemplateData: templateData,
})
}
// userFriendlyTexts sets user friendly texts for a set of known param names and
// returns them as a new set as we don't want to overwrite the template values
func (c *CmdConfigure) userFriendlyTexts(param templates.Param) templates.Param {
result := param
if result.ValueType == "" || (result.ValueType != "" && !funk.ContainsString(templates.ParamValueTypes, result.ValueType)) {
result.ValueType = templates.ParamValueTypeString
}
type replacements struct {
Name string
Help string
Example string
ValueType string
}
resultNameMap := map[string]replacements{
"title": {Name: "UserFriendly_Title_Name", Help: "UserFriendly_Title_Help"},
"device": {Name: "UserFriendly_Device_Name"},
"baudrate": {Name: "UserFriendly_Baudrate_Name"},
"comset": {Name: "UserFriendly_ComSet_Name"},
"host": {Name: "UserFriendly_Host_Name"},
"port": {Name: "UserFriendly_Port_Name", ValueType: templates.ParamValueTypeNumber},
"user": {Name: "UserFriendly_User_Name"},
"password": {Name: "UserFriendly_Password_Name"},
"capacity": {Name: "UserFriendly_Capacity_Name", Example: "41.5", ValueType: templates.ParamValueTypeFloat},
"vin": {Name: "UserFriendly_Vin_Name", Help: "UserFriendly_Vin_Help"},
"cache": {Name: "UserFriendly_Cache_Name", Help: "UserFriendly_Cache_Help", Example: "5m"},
"mode": {Name: "ChargeMode_Question", ValueType: templates.ParamValueTypeChargeModes},
"minsoc": {Name: "UserFriendly_MinSoC_Name", Help: "UserFriendly_MinSoC_Help", Example: "25", ValueType: templates.ParamValueTypeNumber},
"targetsoc": {Name: "UserFriendly_TargetSoC_Name", Help: "UserFriendly_TargetSoC_Help", Example: "80", ValueType: templates.ParamValueTypeNumber},
"mincurrent": {Name: "UserFriendly_MinCurrent_Name", Help: "UserFriendly_MinCurrent_Help", Example: "6", ValueType: templates.ParamValueTypeNumber},
"maxcurrent": {Name: "UserFriendly_MaxCurrent_Name", Help: "UserFriendly_MaxCurrent_Help", Example: "16", ValueType: templates.ParamValueTypeNumber},
"identifiers": {Name: "UserFriendly_Identifier_Name", Help: "UserFriendly_Identifier_Help"},
"standbypower": {Name: "UserFriendly_StandByPower_Name", Help: "UserFriendly_StandByPower_Help", ValueType: templates.ParamValueTypeNumber},
}
if resultMapItem := resultNameMap[strings.ToLower(result.Name)]; resultMapItem != (replacements{}) {
// always overwrite if defined
if resultMapItem.Name != "" {
result.Name = c.localizedString(resultMapItem.Name, nil)
}
if resultMapItem.ValueType != "" {
result.ValueType = resultMapItem.ValueType
}
// only set if empty
if result.Help.String(c.lang) == "" && resultMapItem.Help != "" {
result.Help.SetString(c.lang, c.localizedString(resultMapItem.Help, nil))
}
if result.Example == "" && resultMapItem.Example != "" {
result.Example = resultMapItem.Example
}
}
return result
}

1
go.mod
View file

@ -89,6 +89,7 @@ require (
github.com/thoas/go-funk v0.9.1
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c
github.com/volkszaehler/mbmd v0.0.0-20220108103619-de9b2cf95ebe
github.com/writeas/go-strip-markdown v2.0.1+incompatible
gitlab.com/bboehmke/sunny v0.15.1-0.20211022160056-2fba1c86ade6
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d

2
go.sum
View file

@ -800,6 +800,8 @@ github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAh
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/volkszaehler/mbmd v0.0.0-20220108103619-de9b2cf95ebe h1:pxQR+cX7Wl31hX1YRmYNMx3dWydSWhEk8Pzzv02WguM=
github.com/volkszaehler/mbmd v0.0.0-20220108103619-de9b2cf95ebe/go.mod h1:Re4O+TyZ5p3Z4L0zODnzPyEZofTKF2kgLSmxG79h0Xs=
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

View file

@ -77,7 +77,7 @@ func New(conf map[string]interface{}, site site.API, httpd *server.HTTPd) (*SEMP
var did []byte
if cc.DeviceID == "" {
if did, err = uniqueDeviceID(); err != nil {
if did, err = UniqueDeviceID(); err != nil {
return nil, fmt.Errorf("creating device id: %w", err)
}
} else {
@ -338,7 +338,7 @@ func (s *SEMP) serialNumber(id int) string {
}
// uniqueDeviceID creates a 6-bytes base device id from machine id
func uniqueDeviceID() ([]byte, error) {
func UniqueDeviceID() ([]byte, error) {
bytes := 6
mid, err := machineid.ProtectedID("evcc-semp")

View file

@ -21,9 +21,10 @@ var acceptable = []string{
"no ping response for 192.0.2.2", // SMA
"network is unreachable",
"[1ESY1161052714 1ESY1161229249 1EMH0008842285 1ESY1161978584 1EMH0004864048 1ESY1161979033 7ELS8135823805]", // Discovergy
"can only have either uri or device", // modbus
"(Client.Timeout exceeded while awaiting headers)", // http
"cannot create meter 'discovergy': unexpected status: 401", //Discovergy Proxy
"can only have either uri or device", // modbus
"(Client.Timeout exceeded while awaiting headers)", // http
"cannot create meter 'discovergy': unexpected status: 401", // Discovergy Proxy
"login failed: Put \"https://192.0.2.2/v1/login\": context deadline exceeded", // LG ESS
}
func TestMeterTemplates(t *testing.T) {
@ -43,9 +44,9 @@ func TestMeterTemplates(t *testing.T) {
modbusChoices := tmpl.ModbusChoices()
// we only test one modbus setup
if funk.ContainsString(modbusChoices, templates.ModbusChoiceTCPIP) {
values[templates.ModbusTCPIP] = true
values[templates.ModbusKeyTCPIP] = true
} else {
values[templates.ModbusRS485TCPIP] = true
values[templates.ModbusKeyRS485TCPIP] = true
}
}

View file

@ -16,13 +16,20 @@ The following describes each possible element in a yaml file
`template` expects a unique template name for the current device class (charger, meter, vehicle are device classes)
## `description`
## `products`
`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)
`products` expects a list of products that work with this template.
## `generic`
Each product contains:
`generic: true` defines templates that are typically not a hardware product, but rather generic implementations that can be used by a variety of products (e.g. inverters with sunspec support) or for software components like vzlogger.
- `brand`: an optional brand description of the product
- `description`: an optional description e.g. of the product model. Expects `generic`, `de`, `en`: an optional description of the product
Either `brand`, or `description` need to be set.
## `group`
`group` contains the reference to a `groups.yaml` entry. This is used to group switchable sockets and generic device support (e.g. SunSpec) templates.
## `guidedsetup`
@ -62,23 +69,36 @@ Example Use Case: With SMA Home Manager, there can be a SMA Energy Meter used fo
## `capabilities`
`capabilities` provides an option to define special capabilities of the devie
`capabilities` provides an option to define special capabilities of the device as a list of strings
**Possible Values**:
- `iso151182: true`: If the charger supports communicating via ISO15118-2
- `iso151182`: If the charger supports communicating via ISO15118-2
- `rfid`: If the charger supports RFID
- `1p3p`: If the charger supports 1P/3P-phase switching
- `smahems`: If the device can be used as an SMA HEMS device, only used for the SMA Home Manager 2.0 right now
## `requirements`
`requirements` provides an option to define various requirements / dependencies that need to be setup
### `evcc`
`evcc` is a list of evcc specific system requirements
**Possible Values**:
- `sponsorshipt: true`: If the device requires a sponsorship token
- `eebus: true`: If the device is accessed via the eebus protocol and thus requires the corresponding setup
- `hems: sma`: If the device can be used as an SMA HEMS device, only used for the SMA Home Manager 2.0 right now
- `description`: expects language specific texts via `de`, `en` to provide specific things the user has to do, e.g. minimum firmware versions or specific hardware setup requirements
- `uri`: a link providing more help on the requirements
- `sponsorship`: If the device requires a sponsorship token
- `eebus`: If the device is accessed via the eebus protocol and thus requires the corresponding setup
- `mqtt`: If the device a MQTT setup
### `description`
`description` expects language specific texts via `de`, `en` to provide specific things the user has to do, e.g. minimum firmware versions or specific hardware setup requirements. The content can be multiline and Markdown
### `uri`
`uri` is a link providing more help on the requirements
## `loglevel`
@ -171,7 +191,7 @@ Example Use Case: With SMA Home Manager, there can be a SMA Energy Meter used fo
### `help`
`help` expects language specific help texts via `de`, `en`
`help` expects language specific help texts via `generic` (language independent), `de`, `en`
## `render`

View file

@ -1,8 +1,13 @@
template: abl
description:
generic: ABL eMH / SENEC.Wallbox pro
requirements:
sponsorship: true
products:
- brand: ABL
description:
generic: eMH
- brand: SENEC
description:
generic: Wallbox pro
requirements:
evcc: ["sponsorship"]
params:
- name: modbus
choice: ["rs485"]

View file

@ -0,0 +1,14 @@
template: alfen
products:
- brand: Alfen
description:
generic: Eve
capabilities: ["1p3p"]
requirements:
evcc: ["sponsorship"]
params:
- name: modbus
choice: ["tcpip"]
render: |
type: alfen
{{include "modbus" .}}

View file

@ -0,0 +1,15 @@
template: alphatec
products:
- brand: Alphatec
description:
generic: Wallbox Mini
requirements:
evcc: ["sponsorship"]
params:
- name: modbus
choice: ["rs485"]
baudrate: 9600
comset: "8N1"
render: |
type: alphatec
{{include "modbus" .}}

View file

@ -1,8 +1,10 @@
template: cfos
description:
generic: cFos PowerBrain
requirements:
sponsorship: true
products:
- brand: cFos
description:
generic: PowerBrain
requirements:
evcc: ["sponsorship"]
params:
- name: host
required: true

View file

@ -1,6 +1,8 @@
template: daheimladen
description:
generic: Daheimladen (Cloud API)
products:
- brand: DaheimLaden
description:
generic: Wallbox (Cloud API)
params:
- name: token
help:

View file

@ -1,8 +1,11 @@
template: easee
description:
generic: Easee Home (Cloud API)
requirements:
sponsorship: true
products:
- brand: Easee
description:
generic: Home (Cloud API)
capabilities: ["1p3p"]
requirements:
evcc: ["sponsorship"]
params:
- name: user
required: true

View file

@ -1,12 +1,12 @@
template: eebus
description:
de: EEBUS kompatible Wallbox
en: EEBUS compatible Wallbox
generic: true
capabilities:
iso151182: true
requirements:
eebus: true
products:
- description:
de: EEBUS kompatible Wallbox
en: EEBUS compatible Wallbox
group: generic
capabilities: ["iso151182"]
requirements:
evcc: ["eebus"]
params:
- name: ski
required: true

View file

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

View file

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

View file

@ -1,6 +1,9 @@
template: fritzdect
description:
generic: FritzDECT
products:
- brand: AVM
description:
generic: FritzDECT
group: switchsockets
params:
- name: uri
default: https://fritz.box

View file

@ -0,0 +1,27 @@
template: go-e-v3
products:
- brand: go-e
description:
generic: Charger HOME+ (V3)
- brand: go-e
description:
generic: Charger HOMEfix (V3)
capabilities: ["1p3p"]
requirements:
description:
de: |
Benötigt mindestens Firmware 052.1 oder neuer.
Für 1P/3P-Phasenumschaltung muss die HTTP API v2 im Charger aktiviert sein.
en: |
Requires firmware 052.1 or later.
For 1P/3P-Phase switching the HTTP API v2 in the charger setup needs to be activated.
uri: https://docs.evcc.io/docs/devices/chargers#go-echarger-homeprov3
params:
- name: host
required: true
example: 192.0.2.2
render: |
type: go-e
uri: http://{{ .host }}

View file

@ -1,11 +1,18 @@
template: go-e
description:
generic: go-eCharger Home/Pro/V3
products:
- brand: go-e
description:
generic: Charger HOME+
- brand: go-e
description:
generic: Charger HOMEfix
- brand: go-e
description:
generic: Charger Pro
requirements:
description:
en: Home und PRO require firmware 040.0 or later, V3 requires firmware 052.1 or later.
de: Home und PRO benötigen mindestens Firmware 040.0 oder neuer, V3 benötigt mindestens Firmware 052.1 oder neuer.
uri: https://docs.evcc.io/docs/devices/chargers#go-echarger-homeprov3
en: Require firmware 040.0 or later.
de: Benötigen mindestens Firmware 040.0 oder neuer.
params:
- name: host
required: true

View file

@ -1,8 +1,13 @@
template: heidelberg
description:
generic: Heidelberg Energy Control (Modbus RTU)
requirements:
sponsorship: true
products:
- brand: Heidelberg
description:
generic: Energy Control (Modbus RTU)
- brand: Wahlter Werke
description:
generic: Basic Evo Pro
requirements:
evcc: ["sponsorship"]
params:
- name: modbus
choice: ["rs485"]

View file

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

View file

@ -1,8 +1,10 @@
template: innogy-ebox
description:
generic: Innogy eBox
requirements:
sponsorship: true
products:
- brand: Innogy
description:
generic: eBox
requirements:
evcc: ["sponsorship"]
params:
- name: modbus
choice: ["tcpip"]

View file

@ -1,16 +1,34 @@
template: keba
description:
generic: KEBA Connect
products:
- brand: KEBA
description:
generic: KeContact P20
- brand: KEBA
description:
generic: KeContact P30
- brand: BMW
description:
generic: i Wallbox
capabilities: ["rfid"]
requirements:
description:
en: This requires the UDP function to be enabled with DIP 1.3 = ON, see the installation manual.
de: Es muss eine sogenannte UDP Funktion über den DIP Schalter 1.3 eingeschaltet (ON) werden. Die Installationsanleitung der Wallbox hilft hier weiter.
uri: https://docs.evcc.io/docs/devices/chargers#keba-connect
en: This requires the UDP function to be enabled with DIP 1.3 = ON, see the installation manual.
params:
- name: host
required: true
example: 192.0.2.2
- name: rfid
example: 765765348
advanced: true
help:
de: Die Kennung eines RFID Tags um den Lademodus zu starten, selbst wenn die Wallbox gesperrt ist.
en: A RFID tag ID to enable charging even when the wallbox is locked.
- name: serial
advanced: true
help:
de: Die Seriennummer, ermöglicht es auch mit der Wallbox zu kommunizieren wenn evcc in Docker läuft.
en: The serial number, allows to communicate with the Wallbox when running evcc in docker
render: |
type: keba
uri: {{ .host }}
@ -18,3 +36,6 @@ render: |
rfid:
tag: {{ .rfid }}
{{ end }}
{{ if ne .serial "" }}
serial: {{ .serial }}
{{ end }}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,17 @@
template: pcelectric-garo
products:
- brand: PC Electric
description:
generic: Garo
requirements:
evcc: ["sponsorship"]
description:
de: Es können momentan nur als Master konfigurierte Geräte verwendet werden!
en: Only devices configured as master can be used right now!
params:
- name: host
- name: port
default: 8080
render: |
type: garo
uri: http://{{ .host }}:{{ .port }}/servlet

View file

@ -1,7 +1,8 @@
template: phoenix-em-eth
description:
generic: Phoenix EM-CP-PP-ETH Controller (Modbus TCP)
generic: true
products:
- description:
generic: Phoenix EM-CP-PP-ETH Controller (Modbus TCP)
group: generic
requirements:
description:
en: The Wallbe must be connected using Ethernet and the DIP switch 10 must be set to 'ON'.

View file

@ -1,7 +1,8 @@
template: phoenix-ev-eth
description:
generic: Phoenix EV-ETH Controller (Modbus TCP)
generic: true
products:
- description:
generic: Phoenix EV-ETH Controller (Modbus TCP)
group: generic
requirements:
description:
en: The Wallbe must be connected using Ethernet and the DIP switch 10 must be set to 'ON'.

View file

@ -1,7 +1,8 @@
template: phoenix-ev-ser
description:
generic: Phoenix EV-SER Controller (Modbus RTU)
generic: true
products:
- description:
generic: Phoenix EV-SER Controller (Modbus RTU)
group: generic
params:
- name: modbus
choice: ["rs485"]

View file

@ -1,10 +1,11 @@
template: pmcc
description:
generic: Porsche Mobile Charger Connect
capabilities:
iso151182: true
requirements:
eebus: true
products:
- brand: Porsche
description:
generic: Mobile Charger Connect
capabilities: ["iso151182"]
requirements:
evcc: ["eebus"]
params:
- name: ski
required: true

View file

@ -1,8 +1,10 @@
template: pmcp
description:
generic: Porsche Mobile Charger Plus
requirements:
eebus: true
products:
- brand: Porsche
description:
generic: Mobile Charger Plus
requirements:
evcc: ["eebus"]
params:
- name: ski
required: true

View file

@ -1,6 +1,8 @@
template: shelly
description:
generic: Shelly
products:
- description:
generic: Shelly
group: switchsockets
params:
- name: host
required: true

View file

@ -1,6 +1,8 @@
template: tasmota
description:
generic: Tasmota
products:
- description:
generic: Tasmota
group: switchsockets
params:
- name: host
required: true

View file

@ -1,6 +1,8 @@
template: tinkerforge-warp-pro
description:
generic: TinkerForge WARP Charger Pro
products:
- brand: TinkerForge
description:
generic: 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,6 +1,8 @@
template: tinkerforge-warp
description:
generic: TinkerForge WARP Charger
products:
- brand: TinkerForge
description:
generic: 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,6 +1,9 @@
template: tplink
description:
generic: TP-LINK Smart Plug
products:
- brand: TP-LINK
description:
generic: Smart Plug
group: switchsockets
params:
- name: host
required: true

View file

@ -1,7 +1,13 @@
template: wallbe-meter
description:
de: Wallbe (Eco, Pro) mit Strommessgerät
en: Wallbe (Eco, Pro) with Meter
products:
- brand: Wallbe
description:
de: Eco (mit Strommessgerät)
en: Eco (with Meter)
- brand: Wallbe
description:
de: Pro (mit Strommessgerät)
en: 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,7 +1,13 @@
template: wallbe-pre2019-meter
description:
de: Wallbe (Eco, Pro) (vor ~2019) mit Strommessgerät
en: Wallbe (Eco, Pro) (pre ~2019) with Meter
products:
- brand: Wallbe
description:
de: Eco (vor ~2019, mit Strommessgerät)
en: Eco (pre ~2019, with Meter)
- brand: Wallbe
description:
de: Pro (vor ~2019, mit Strommessgerät)
en: 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,7 +1,13 @@
template: wallbe-pre2019
description:
de: Wallbe (Eco, Pro) (vor ~2019)
en: Wallbe (Eco, Pro) (pre ~2019)
products:
- brand: Wallbe
description:
de: Eco (vor ~2019)
en: Eco (pre ~2019)
- brand: Wallbe
description:
de: Pro (vor ~2019)
en: 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,6 +1,11 @@
template: wallbe
description:
generic: Wallbe (Eco, Pro)
products:
- brand: Wallbe
description:
generic: Eco
- brand: Wallbe
description:
generic: Pro
requirements:
description:
en: The Wallbe must be connected using Ethernet and the DIP switch 10 must be set to 'ON'.

View file

@ -0,0 +1,286 @@
params:
- name: title
description:
de: Titel
en: Title
help:
de: Wird in der Benutzeroberfläche angezeigt
en: Will be displayed in the user interface
- name: modbusid
description:
generic: Modbus ID
default: 1
valuetype: number
- name: modbusdevice
description:
de: Geräteadresse
en: Device address
help:
de: USB-RS485 Adapter Adresse
en: USB-RS485 Adapter address
example: /dev/ttyUSB0
- name: modbusbaudrate
description:
de: Baudrate
en: Baudrate
help:
de: Prüfe die Geräteeinstellungen, typische Werte sind 9600, 19200, 38400, 57600, 115200
en: Check the device settings, typical values are 9600, 19200, 38400, 57600, 115200
default: 9600
valuetype: number
- name: modbuscomset
description:
de: ComSet
en: ComSet
help:
de: Kommunikationsparameter für den Adapter
en: Communication parameter for the adapter
default: "8N1"
- name: host
description:
de: IP-Adresse oder Hostname
en: IP-Address or Hostname
help:
de: IP-Adresse oder Hostname
en: IP-Address or Hostname
example: "192.0.2.2"
- name: port
description:
de: Port
en: Port
help:
de: Port
en: Port
valuetype: number
- name: user
description:
de: Benutzername
en: Username
help:
de: Benutzername um auf das Gerät zuzugreifen
en: Username to access the device
- name: password
description:
de: Passwort
en: Password
help:
de: Passwort des Benutzerkontos
en: Password of the account
mask: true
- name: capacity
description:
de: Akku-Kapazität in kWh
en: Battery-Capacity in kWh
help:
de: Akku-Kapazität in kWh
en: Battery-Capacity in kWh
example: '41.5'
valuetype: float
- name: vin
description:
de: Fahrzeugidentifikationsnummer
en: Vehicle Identification Number
help:
de: Erforderlich, wenn mehrere Fahrzeuge des Herstellers vorhanden sind
en: Required if you own multiple vehicles of the same brand
example: W...
- name: cache
description:
de: Cache
en: Cache
help:
de: Das zeitliche Interval, in welchem Daten vom Fahrzeug neu geladen werden soll
en: Time interval with when data should be reloaded from the vehicle
example: 5m
- name: mode
description:
de: Standard-Lademodus, wenn ein Fahrzeug angeschlossen ist
en: Default charging mode when a vehicle is connected
help:
de: Möglich sind Off, Now, MinPV und PV, oder leer wenn keiner definiert werden soll
en: Possible values are Off, Now, MinPV and PV, or empty if none should be set
valuetype: chargemodes
- name: minsoc
description:
de: Minimaler Ladestand (SoC) in %
en: Minimum state of charge (SoC) in %
help:
de: Lade sofort mit maximaler Geschwindigkeit bis zu dem angegeben Ladestand, wenn der Lademodus nicht auf 'Aus' steht
en: Charge immediately with maximum power up to the defined state of charge, if the charge mode is not set to 'OFF'
example: 25
valuetype: number
- name: targetsoc
description:
de: Ziel-Ladestand (SoC) in %
en: Target state of charge (SoC) in %
help:
de: Bis zu welchem Ladestand (SoC) soll das Fahrzeug geladen werden
en: Until which state of charge (SoC) should the vehicle be charged
example: 80
valuetype: number
- name: mincurrent
description:
de: Minimale Stromstärke in Ampere (A)
en: Minimum amperage (A)
help:
de: Definiert die minimale Stromstärke pro angeschlossener Phase mit welcher das Fahrzeug geladen werden soll
en: The minimum amperage per connected phase with which the car should be charged
example: 6
valuetype: number
- name: maxcurrent
description:
de: Maximale Stromstärke in Ampere (A)
en: Maximum amperage (A)
help:
de: Definiert die maximale Stromstärke pro angeschlossener Phase mit welcher das Fahrzeug geladen werden soll
en: The maximum amperage per connected phase with which the car shuold be charged
example: 16
valuetype: number
- name: identifiers
description:
de: Identifikationsnummer
en: Identification
help:
de: "Kann meist erst später eingetragen werden, siehe: https://docs.evcc.io/docs/guides/vehicles/#erkennung-des-fahrzeugs-an-der-wallbox"
en: "Mostly this can be added later, see: https://docs.evcc.io/docs/guides/vehicles/#erkennung-des-fahrzeugs-an-der-wallbox"
valuetype: stringlist
- name: standbypower
description:
de: Standby-Leistung in W"
en: Standby power in W
help:
de: Leistung oberhalb des angegebenen Wertes wird als Ladeleistung gewertet
en: Power values above this value will be considered as charging pow
valuetype: number
presets:
vehiclebase:
params:
- name: title
- name: user
required: true
- name: password
required: true
- name: vin
- name: capacity
- name: cache
advanced: true
render: |
{{define "vehicle-base"}}
{{- if ne .title "" }}
title: {{ .title }}
{{- end }}
user: {{ .user }}
password: {{ .password }}
{{- if ne .capacity "" }}
capacity: {{ .capacity }}
{{- end }}
{{- if ne .vin "" }}
vin: {{ .vin }}
{{- end }}
{{- if ne .cache "" }}
cache: {{ .cache }}
{{- end }}
{{end}}
vehicleidentify:
params:
- name: mode
advanced: true
- name: minSoC
advanced: true
- name: targetSoC
advanced: true
- name: minCurrent
advanced: true
- name: maxCurrent
advanced: true
- name: identifiers
advanced: true
valueType: stringlist
render: |
{{define "vehicle-identify"}}
{{- if or (ne .mode "") (ne .minSoC "") (ne .targetSoC "") (ne .minCurrent "") (ne .maxCurrent "") }}
onIdentify:
{{- if (ne .mode "") }}
mode: {{ .mode }}
{{- end }}
{{- if (ne .minSoC "") }}
minSoC: {{ .minSoC }}
{{- end }}
{{- if (ne .targetSoC "") }}
targetSoC: {{ .targetSoC }}
{{- end }}
{{- if (ne .minCurrent "") }}
minCurrent: {{ .minCurrent }}
{{- end }}
{{- if (ne .maxCurrent "") }}
maxCurrent: {{ .maxCurrent }}
{{- end }}
{{- end }}
{{- if ne (len .identifiers) 0 }}
identifiers:
{{- range .identifiers }}
- {{ . }}
{{- end }}
{{- end }}
{{end}}
modbus:
interfaces:
rs485: ["rs485serial", "rs485tcpip"]
tcpip: ["tcpip"]
types:
rs485serial:
description:
generic: Serial (USB-RS485 Adapter)
params:
- reference: true
referencename: modbusid
name: id
- reference: true
referencename: modbusdevice
name: device
- reference: true
referencename: modbusbaudrate
name: baudrate
- reference: true
referencename: modbuscomset
name: comset
rs485tcpip:
description:
generic: Serial (Ethernet-RS485 Adapter)
params:
- reference: true
referencename: modbusid
name: id
- reference: true
name: host
- reference: true
name: port
default: 502
- name: rtu
default: true
hidden: true
tcpip:
description:
generic: TCP/IP
params:
- reference: true
referencename: modbusid
name: id
- reference: true
name: host
- reference: true
name: port
default: 502
devicegroups:
generic:
de: Generische Unterstützung
en: generic support
switchsockets:
de: Schaltbare Steckdosen
en: switchable sockets
sockets:
de: Steckdosen
en: power sockets

View file

@ -6,6 +6,6 @@ var (
//go:embed charger/*.yaml meter/*.yaml vehicle/*.yaml
YamlTemplates embed.FS
//go:embed parambaselist.yaml
ParamBaseListDefinition string
//go:embed defaults.yaml
DefaultsContent string
)

View file

@ -1,8 +1,10 @@
template: cfos
description:
generic: cFos PowerBrain Meter
requirements:
sponsorship: true
products:
- brand: cFos
description:
generic: PowerBrain Meter
requirements:
evcc: ["sponsorship"]
params:
- name: usage
choice: [ "charge" ]

View file

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

View file

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

View file

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

View file

@ -1,6 +1,9 @@
template: fritzdect
description:
generic: FritzDECT
products:
- brand: AVM
description:
generic: FritzDECT
group: sockets
params:
- name: usage
choice: ["pv", "charge"]

View file

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

View file

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

View file

@ -0,0 +1,103 @@
template: huawei-dongle-powersensor
products:
- brand: Huawei
description:
generic: SUN2000 with Sdongle & Power Sensor
guidedsetup:
enable: true
params:
- name: usage
choice: [ "grid", "pv", "battery" ]
- name: modbus
choice: ["tcpip"]
render: |
type: custom
{{- if eq .usage "grid" }}
power:
source: modbus
{{include "modbus" . | indent 2}}
model: huawei
timeout: 30s
connectdelay: 5
delay: 2s
register:
address: 37113 # Grid import export power
type: holding
decode: int32
scale: -1
energy:
source: modbus
{{include "modbus" . | indent 2}}
model: huawei
timeout: 30s
connectdelay: 5
delay: 2s
register:
address: 37121 # Grid active energy
type: holding
decode: int32
scale: 0.01
currents:
- source: modbus
{{include "modbus" . | indent 2}}
model: huawei
register:
address: 37107 # Huawei phase A grid current
type: holding
decode: int32
scale: -0.01
- source: modbus
{{include "modbus" . | indent 2}}
model: huawei
register:
address: 37109 # Huawei phase B grid current
type: holding
decode: int32
scale: -0.01
- source: modbus
{{include "modbus" . | indent 2}}
model: huawei
register:
address: 37111 # Huawei phase C grid current
type: holding
decode: int32
scale: -0.01
{{- end }}
{{- if eq .usage "pv" }}
power:
source: modbus
{{include "modbus" . | indent 2}}
model: huawei
timeout: 30s
connectdelay: 5
delay: 2s
register:
address: 32080 # Active generation power
type: holding
decode: int32
{{- end }}
{{- if eq .usage "battery" }}
power:
source: modbus
{{include "modbus" . | indent 2}}
model: huawei
timeout: 30s
connectdelay: 5
delay: 2s
register:
address: 37001
type: holding
decode: int32
soc:
source: modbus
{{include "modbus" . | indent 2}}
model: huawei
timeout: 30s
connectdelay: 5
delay: 2s
register:
address: 37004
type: holding
decode: uint16
scale: 0.1
{{- end }}

View file

@ -0,0 +1,25 @@
template: huawei-dongle
products:
- brand: Huawei
description:
generic: SUN2000 with Sdongle
guidedsetup:
enable: true
params:
- name: usage
choice: [ "pv" ]
- name: modbus
choice: ["tcpip"]
render: |
type: custom
power:
source: modbus
{{include "modbus" . | indent 2}}
model: huawei
timeout: 30s
connectdelay: 5s
delay: 2s
register:
address: 32080 # Active generation power
type: holding
decode: int32

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,11 @@
template: kostal-piko
description:
generic: Kostal Piko + Piko BA
products:
- brand: Kostal
description:
generic: Piko
- brand: Kostal
description:
generic: Piko BA
guidedsetup:
enable: true
params:

View file

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

View file

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

View file

@ -1,10 +1,11 @@
template: mqtt-meter
description:
de: MQTT Strommessgerät
en: MQTT Meter
generic: true
requirements:
mqtt: true
products:
- description:
de: MQTT Strommessgerät
en: MQTT Meter
group: generic
requirements:
evcc: ["mqtt"]
params:
- name: usage
choice: [ "grid", "pv", "charge", "battery" ]

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,9 @@
template: shelly-3em
description:
generic: Shelly 3EM
products:
- brand: Shelly
description:
generic: 3EM
group: sockets
params:
- name: usage
choice: [ "grid" ]

View file

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

View file

@ -1,8 +1,9 @@
template: sma-home-manager
description:
generic: SMA Sunny Home Manager 2.0
requirements:
hems: sma
products:
- brand: SMA
description:
generic: Sunny Home Manager 2.0
capabilities: ["smahems"]
guidedsetup:
enable: true
linked:

View file

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

View file

@ -1,6 +1,8 @@
template: solaredge-hybrid
description:
generic: Solaredge Hybrid
products:
- brand: Solaredge
description:
generic: Hybrid
guidedsetup:
enable: true
requirements:

View file

@ -1,17 +1,27 @@
template: solaredge-inverter
description:
de: Solaredge Wechselrichter
en: Solaredge Inverter
products:
- brand: Solaredge
description:
de: Wechselrichter
en: Inverter
guidedsetup:
enable: true
params:
- name: usage
choice: [ "pv" ]
choice: [ "grid", "pv" ]
- name: modbus
choice: [ "tcpip" ]
id: 1
port: 1502
- name: timeout
default: 3s
render: |
type: modbus
model: sunspec
{{ include "modbus" . }}
timeout: {{ .timeout }}
{{- if eq .usage "grid" }}
subdevice: 1
power: 203:W # sunspec 3-phase meter power reading
scale: -1
{{- end -}}

View file

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

View file

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

View file

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

View file

@ -1,6 +1,8 @@
template: sungrow
description:
generic: Sungrow (Beta)
products:
- brand: Sungrow
description:
generic: Inverter (Beta)
guidedsetup:
enable: true
params:

View file

@ -1,8 +1,9 @@
template: sunspec
description:
de: Wechselrichter (SunSpec)
en: Inverter (SunSpec)
generic: true
products:
- description:
de: Wechselrichter (SunSpec)
en: Inverter (SunSpec)
group: generic
params:
- name: usage
choice: [ "grid", "pv", "battery" ]
@ -13,6 +14,7 @@ render: |
model: sunspec
{{ include "modbus" .}}
{{- if eq .usage "grid" }}
subdevice: 1
power: 203:W # sunspec 3-phase meter power reading
{{- end -}}
{{- if eq .usage "battery" }}

View file

@ -0,0 +1,21 @@
template: tasmota
products:
- description:
generic: Tasmota
group: sockets
params:
- name: usage
choice: [ "grid", "pv", "charge" ]
- name: host
required: true
example: 192.0.2.2
render: |
type: custom
power: # power reading (W)
source: http
uri: http://{{ .host }}/cm?cmnd=Status%208
jq: .StatusSNS.ENERGY.Power
energy: # energy reading (Wh)
source: http
uri: http://{{ .host }}/cm?cmnd=Status%208
jq: .StatusSNS.ENERGY.Total * 1000

View file

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

View file

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

View file

@ -1,6 +1,8 @@
template: victron-energy
description:
generic: Victron Energy
products:
- brand: Victron
description:
generic: Energy
guidedsetup:
enable: true
params:

View file

@ -1,7 +1,8 @@
template: volkszaehler-http
description:
generic: Volkszähler (HTTP API)
generic: true
products:
- description:
generic: Volkszähler (HTTP API)
group: generic
params:
- name: usage
choice: [ "grid" ]

View file

@ -1,7 +1,8 @@
template: volkszaehler-importexport
description:
generic: Volkszähler (HTTP API, Import & Export)
generic: true
products:
- description:
generic: Volkszähler (HTTP API, Import & Export)
group: generic
params:
- name: usage
choice: [ "grid" ]

View file

@ -1,7 +1,8 @@
template: volkszaehler-ws
description:
generic: Volkszähler (WebSocket API)
generic: true
products:
- description:
generic: Volkszähler (WebSocket API)
group: generic
params:
- name: usage
choice: [ "grid" ]

View file

@ -1,7 +1,8 @@
template: vzlogger
description:
generic: vzlogger
generic: true
products:
- description:
generic: vzlogger
group: generic
params:
- name: usage
choice: [ "grid" ]

View file

@ -1,73 +0,0 @@
vehiclebase:
params:
- name: title
- name: user
required: true
- name: password
required: true
mask: true
- name: vin
example: W...
- name: capacity
default: '50'
- name: cache
advanced: true
render: |
{{define "vehicle-base"}}
{{- if ne .title "" }}
title: {{ .title }}
{{- end }}
user: {{ .user }}
password: {{ .password }}
{{- if ne .capacity "" }}
capacity: {{ .capacity }}
{{- end }}
{{- if ne .vin "" }}
vin: {{ .vin }}
{{- end }}
{{- if ne .cache "" }}
cache: {{ .cache }}
{{- end }}
{{end}}
vehicleidentify:
params:
- name: mode
advanced: true
- name: minSoC
advanced: true
- name: targetSoC
advanced: true
- name: minCurrent
advanced: true
- name: maxCurrent
advanced: true
- name: identifiers
advanced: true
valueType: stringlist
render: |
{{define "vehicle-identify"}}
{{- if or (ne .mode "") (ne .minSoC "") (ne .targetSoC "") (ne .minCurrent "") (ne .maxCurrent "") }}
onIdentify:
{{- if (ne .mode "") }}
mode: {{ .mode }}
{{- end }}
{{- if (ne .minSoC "") }}
minSoC: {{ .minSoC }}
{{- end }}
{{- if (ne .targetSoC "") }}
targetSoC: {{ .targetSoC }}
{{- end }}
{{- if (ne .minCurrent "") }}
minCurrent: {{ .minCurrent }}
{{- end }}
{{- if (ne .maxCurrent "") }}
maxCurrent: {{ .maxCurrent }}
{{- end }}
{{- end }}
{{- if ne (len .identifiers) 0 }}
identifiers:
{{- range .identifiers }}
- {{ . }}
{{- end }}
{{- end }}
{{end}}

View file

@ -1,9 +1,9 @@
template: audi
description:
generic: Audi
products:
- brand: Audi
params:
- base: vehiclebase
- base: vehicleidentify
- preset: vehiclebase
- preset: vehicleidentify
- name: vin
example: WAUZZZ...
- name: capacity

View file

@ -1,9 +1,9 @@
template: bmw
description:
generic: BMW
products:
- brand: BMW
params:
- base: vehiclebase
- base: vehicleidentify
- preset: vehiclebase
- preset: vehicleidentify
- name: vin
example: WBMW...
- name: capacity

View file

@ -1,9 +1,11 @@
template: carwings
description:
generic: Nissan Leaf (pre 2019)
products:
- brand: Nissan
description:
generic: Leaf (pre 2019)
params:
- base: vehiclebase
- base: vehicleidentify
- preset: vehiclebase
- preset: vehicleidentify
- name: capacity
default: 50
render: |

View file

@ -1,9 +1,9 @@
template: citroen
description:
generic: Citroen
products:
- brand: Citroen
params:
- base: vehiclebase
- base: vehicleidentify
- preset: vehiclebase
- preset: vehicleidentify
- name: capacity
default: 50
render: |

View file

@ -1,9 +1,9 @@
template: dacia
description:
generic: Dacia
products:
- brand: Dacia
params:
- base: vehiclebase
- base: vehicleidentify
- preset: vehiclebase
- preset: vehicleidentify
- name: capacity
default: 27.4
render: |

View file

@ -1,9 +1,9 @@
template: ds
description:
generic: DS
products:
- brand: DS
params:
- base: vehiclebase
- base: vehicleidentify
- preset: vehiclebase
- preset: vehicleidentify
- name: capacity
default: 50
render: |

View file

@ -1,7 +1,8 @@
template: evnotify
description:
generic: evNotify
generic: true
products:
- description:
generic: evNotify
group: generic
params:
- name: title
- name: akey
@ -11,7 +12,7 @@ params:
- name: capacity
default: 64
valuetype: float
- base: vehicleidentify
- preset: vehicleidentify
render: |
type: custom
{{- if ne .title "" }}

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