diff --git a/assets/js/components/GlobalSettingsModal.vue b/assets/js/components/GlobalSettingsModal.vue
index c03a3dfb7..6fa7f72ee 100644
--- a/assets/js/components/GlobalSettingsModal.vue
+++ b/assets/js/components/GlobalSettingsModal.vue
@@ -83,6 +83,26 @@
+
+
+
@@ -99,6 +119,7 @@ import { getLocalePreference, setLocalePreference, LOCALES, removeLocalePreferen
import { getThemePreference, setThemePreference, THEMES } from "../theme";
import { getUnits, setUnits, UNITS } from "../units";
import { getGridDetails, setGridDetails, GRID_DETAILS } from "../gridDetails";
+import { getHiddenFeatures, setHiddenFeatures } from "../featureflags";
export default {
name: "GlobalSettingsModal",
@@ -114,6 +135,7 @@ export default {
language: getLocalePreference() || "",
unit: getUnits(),
gridDetails: getGridDetails(),
+ hiddenFeatures: getHiddenFeatures(),
THEMES,
UNITS,
GRID_DETAILS,
@@ -128,6 +150,9 @@ export default {
locales.sort((a, b) => (a.name < b.name ? -1 : 1));
return locales;
},
+ isNightly: () => {
+ return !!window.evcc.commit;
+ },
},
watch: {
unit(value) {
@@ -139,6 +164,9 @@ export default {
theme(value) {
setThemePreference(value);
},
+ hiddenFeatures(value) {
+ setHiddenFeatures(value);
+ },
language(value) {
const i18n = this.$root.$i18n;
if (value) {
diff --git a/assets/js/components/Loadpoint.vue b/assets/js/components/Loadpoint.vue
index 61da4ac00..72bbbba64 100644
--- a/assets/js/components/Loadpoint.vue
+++ b/assets/js/components/Loadpoint.vue
@@ -201,7 +201,7 @@ export default {
return this.collectProps(LoadpointSettingsModal);
},
settingsButtonVisible: function () {
- return this.$hiddenFeatures || [0, 1, 3].includes(this.phasesConfigured);
+ return this.$hiddenFeatures() || [0, 1, 3].includes(this.phasesConfigured);
},
vehicle: function () {
return this.collectProps(Vehicle);
diff --git a/assets/js/components/LoadpointSettingsModal.vue b/assets/js/components/LoadpointSettingsModal.vue
index 9b44edc2f..21354e97d 100644
--- a/assets/js/components/LoadpointSettingsModal.vue
+++ b/assets/js/components/LoadpointSettingsModal.vue
@@ -152,7 +152,7 @@
-
+
-
@@ -28,9 +28,9 @@
import "@h2d2/shopicons/es/regular/arrowup";
import TopNavigation from "./TopNavigation.vue";
import Notifications from "./Notifications.vue";
+import VehcileSettingsModal from "./VehicleSettingsModal.vue";
import Energyflow from "./Energyflow/Energyflow.vue";
import Loadpoints from "./Loadpoints.vue";
-import Vehicles from "./Vehicles.vue";
import Footer from "./Footer.vue";
import formatter from "../mixins/formatter";
import collector from "../mixins/collector";
@@ -43,7 +43,7 @@ export default {
Footer,
Notifications,
TopNavigation,
- Vehicles,
+ VehcileSettingsModal,
},
mixins: [formatter, collector],
props: {
diff --git a/assets/js/components/Vehicle.vue b/assets/js/components/Vehicle.vue
index 1c314b346..37cd6ffa9 100644
--- a/assets/js/components/Vehicle.vue
+++ b/assets/js/components/Vehicle.vue
@@ -54,9 +54,6 @@
@target-energy-updated="targetEnergyUpdated"
/>
-
- vor 5 Stunden
-
diff --git a/assets/js/components/VehicleOptions.vue b/assets/js/components/VehicleOptions.vue
index 77e2f0709..4cb19ffc9 100644
--- a/assets/js/components/VehicleOptions.vue
+++ b/assets/js/components/VehicleOptions.vue
@@ -23,6 +23,17 @@
{{ $t("main.vehicle.unknown") }}
+
+
+
+
+
+
+
+
+
@@ -30,6 +41,7 @@
diff --git a/assets/js/components/VehicleSettingsModal.vue b/assets/js/components/VehicleSettingsModal.vue
new file mode 100644
index 000000000..32db982ec
--- /dev/null
+++ b/assets/js/components/VehicleSettingsModal.vue
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ configYaml }}
+
+
+ {{ testResult }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/js/components/Vehicles.vue b/assets/js/components/Vehicles.vue
deleted file mode 100644
index 480e02d61..000000000
--- a/assets/js/components/Vehicles.vue
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
-
{{ $t("main.vehicles") }}
-
-
-
-
-
-
-
-
-
-
-
diff --git a/assets/js/experimental.js b/assets/js/experimental.js
new file mode 100644
index 000000000..8a53747fc
--- /dev/null
+++ b/assets/js/experimental.js
@@ -0,0 +1,27 @@
+import settings from "./settings";
+
+const KM = "km";
+const MILES = "mi";
+export const UNITS = [KM, MILES];
+
+const MILES_FACTOR = 0.6213711922;
+
+function isMiles() {
+ return settings.unit === MILES;
+}
+
+export function distanceValue(value) {
+ return isMiles() ? value * MILES_FACTOR : value;
+}
+
+export function distanceUnit() {
+ return isMiles() ? "mi" : "km";
+}
+
+export function getUnits() {
+ return isMiles() ? MILES : KM;
+}
+
+export function setUnits(value) {
+ settings.unit = value;
+}
diff --git a/assets/js/featureflags.js b/assets/js/featureflags.js
index e61de4290..7932143e9 100644
--- a/assets/js/featureflags.js
+++ b/assets/js/featureflags.js
@@ -1,5 +1,15 @@
+import settings from "./settings";
+
+export function setHiddenFeatures(value) {
+ settings.hiddenFeatures = value;
+}
+
+export function getHiddenFeatures() {
+ return settings.hiddenFeatures;
+}
+
export default {
install: (app) => {
- app.config.globalProperties.$hiddenFeatures = window.localStorage["hidden_features"] === "true";
+ app.config.globalProperties.$hiddenFeatures = getHiddenFeatures;
},
};
diff --git a/assets/js/settings.js b/assets/js/settings.js
index 903148474..fbe3b0eae 100644
--- a/assets/js/settings.js
+++ b/assets/js/settings.js
@@ -3,6 +3,7 @@ import { reactive, watch } from "vue";
const SETTINGS_LOCALE = "settings_locale";
const SETTINGS_THEME = "settings_theme";
const SETTINGS_UNIT = "settings_unit";
+const SETTINGS_HIDDEN_FEATURES = "settings_hidden_features";
const SETTINGS_ENERGYFLOW_DETAILS = "settings_energyflow_details";
const SETTINGS_GRID_DETAILS = "settings_grid_details";
@@ -40,6 +41,7 @@ const settings = reactive({
theme: read(SETTINGS_THEME),
unit: read(SETTINGS_UNIT),
gridDetails: read(SETTINGS_GRID_DETAILS),
+ hiddenFeatures: readBool(SETTINGS_HIDDEN_FEATURES),
energyflowDetails: readBool(SETTINGS_ENERGYFLOW_DETAILS),
});
@@ -47,6 +49,7 @@ watch(() => settings.locale, save(SETTINGS_LOCALE));
watch(() => settings.theme, save(SETTINGS_THEME));
watch(() => settings.unit, save(SETTINGS_UNIT));
watch(() => settings.gridDetails, save(SETTINGS_GRID_DETAILS));
+watch(() => settings.hiddenFeatures, saveBool(SETTINGS_HIDDEN_FEATURES));
watch(() => settings.energyflowDetails, saveBool(SETTINGS_ENERGYFLOW_DETAILS));
export default settings;
diff --git a/cmd/configure/configure.go b/cmd/configure/configure.go
index a04b270bc..b3ce319e7 100644
--- a/cmd/configure/configure.go
+++ b/cmd/configure/configure.go
@@ -88,7 +88,7 @@ func (c *Configure) DevicesOfClass(class templates.Class) []device {
case templates.Vehicle:
return c.config.Vehicles
default:
- panic("invalid class: " + class)
+ panic("invalid class: " + class.String())
}
}
diff --git a/cmd/configure/helper.go b/cmd/configure/helper.go
index 1da400d04..bb782956f 100644
--- a/cmd/configure/helper.go
+++ b/cmd/configure/helper.go
@@ -431,8 +431,8 @@ func (c *CmdConfigure) processParams(templateItem *templates.Template, deviceCat
continue
}
- switch param.ValueType {
- case templates.ParamValueTypeStringList:
+ switch param.Type {
+ case templates.ParamTypeStringList:
values := c.processListInputConfig(param)
var nonEmptyValues []string
for _, value := range values {
@@ -493,13 +493,13 @@ func (c *CmdConfigure) processInputConfig(param templates.Param) string {
defaultValue: param.Default,
exampleValue: param.Example,
help: help,
- valueType: param.ValueType,
+ valueType: param.Type,
validValues: param.ValidValues,
mask: param.IsMask(),
required: param.IsRequired(),
})
- if param.ValueType == templates.ParamValueTypeBool && value == "true" {
+ if param.Type == templates.ParamTypeBool && value == "true" {
if err := c.processParamRequirements(param); err != nil {
return "false"
}
diff --git a/cmd/configure/main.go b/cmd/configure/main.go
index c6625212f..7ca30b1a6 100644
--- a/cmd/configure/main.go
+++ b/cmd/configure/main.go
@@ -352,7 +352,7 @@ func (c *CmdConfigure) configureLoadpoints() {
fmt.Println()
minAmperage := c.askValue(question{
label: c.localizedString("Loadpoint_WallboxMinAmperage", nil),
- valueType: templates.ParamValueTypeNumber,
+ valueType: templates.ParamTypeNumber,
minNumberValue: int64(minValue),
maxNumberValue: 32,
required: true,
@@ -360,7 +360,7 @@ func (c *CmdConfigure) configureLoadpoints() {
loadpoint.MinCurrent, _ = strconv.Atoi(minAmperage)
maxAmperage := c.askValue(question{
label: c.localizedString("Loadpoint_WallboxMaxAmperage", nil),
- valueType: templates.ParamValueTypeNumber,
+ valueType: templates.ParamTypeNumber,
minNumberValue: 6,
maxNumberValue: 32,
required: true,
@@ -402,7 +402,7 @@ func (c *CmdConfigure) configureLoadpoints() {
case 3:
amperage := c.askValue(question{
label: c.localizedString("Loadpoint_WallboxMaxAmperage", nil),
- valueType: templates.ParamValueTypeNumber,
+ valueType: templates.ParamTypeNumber,
minNumberValue: int64(minValue),
maxNumberValue: 32,
required: true,
@@ -419,12 +419,12 @@ func (c *CmdConfigure) configureLoadpoints() {
}
fmt.Println()
- loadpoint.Mode = c.askValue(question{valueType: templates.ParamValueTypeChargeModes, excludeNone: true})
+ loadpoint.Mode = c.askValue(question{valueType: templates.ParamTypeChargeModes, excludeNone: true})
fmt.Println()
loadpoint.ResetOnDisconnect = c.askValue(question{
label: c.localizedString("Loadpoint_ResetOnDisconnect", nil),
- valueType: templates.ParamValueTypeBool,
+ valueType: templates.ParamTypeBool,
})
c.configuration.AddLoadpoint(loadpoint)
diff --git a/cmd/configure/survey.go b/cmd/configure/survey.go
index 1b97b33ae..f297c71d4 100644
--- a/cmd/configure/survey.go
+++ b/cmd/configure/survey.go
@@ -122,7 +122,7 @@ func (c *CmdConfigure) askBoolValue(label string) string {
// askValue asks for value input for a given question (template param)
func (c *CmdConfigure) askValue(q question) string {
- if q.valueType == templates.ParamValueTypeBool {
+ if q.valueType == templates.ParamTypeBool {
label := q.label
if q.help != "" {
helpDescription := stripmd.Strip(q.help)
@@ -135,7 +135,7 @@ func (c *CmdConfigure) askValue(q question) string {
return c.askBoolValue(label)
}
- if q.valueType == templates.ParamValueTypeChargeModes {
+ if q.valueType == templates.ParamTypeChargeModes {
chargingModes := []string{string(api.ModeOff), string(api.ModeNow), string(api.ModeMinPV), string(api.ModePV)}
chargeModes := []string{
c.localizedString("ChargeModeOff", nil),
@@ -169,14 +169,14 @@ func (c *CmdConfigure) askValue(q question) string {
return nil
}
- if q.valueType == templates.ParamValueTypeFloat {
+ if q.valueType == templates.ParamTypeFloat {
_, err := strconv.ParseFloat(value, 64)
if err != nil {
return errors.New(c.localizedString("ValueError_Float", nil))
}
}
- if q.valueType == templates.ParamValueTypeNumber {
+ if q.valueType == templates.ParamTypeNumber {
intValue, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return errors.New(c.localizedString("ValueError_Number", nil))
@@ -189,7 +189,7 @@ func (c *CmdConfigure) askValue(q question) string {
}
}
- if q.valueType == templates.ParamValueTypeDuration {
+ if q.valueType == templates.ParamTypeDuration {
_, err := time.ParseDuration(value)
if err != nil {
return errors.New(c.localizedString("ValueError_Duration", nil))
diff --git a/i18n/en.toml b/i18n/en.toml
index 54d4099b5..159053ebc 100644
--- a/i18n/en.toml
+++ b/i18n/en.toml
@@ -158,9 +158,11 @@ label = "Limit"
noLimit = "none"
[main.vehicle]
-changeVehicle = "Change Vehicle"
+addVehicle = "Add vehicle"
+changeVehicle = "Change vehicle"
detectionActive = "Detecting vehicle…"
fallbackName = "Vehicle"
+moreActions = "More actions"
none = "No vehicle"
targetSoc = "Limit"
unknown = "Guest vehicle"
@@ -236,6 +238,10 @@ label = "Grid details"
none = "none"
price = "price"
+[settings.hiddenFeatures]
+label = "Experimental"
+value = "Show experimental UI features."
+
[settings.language]
auto = "Automatic"
label = "Language"
@@ -265,3 +271,8 @@ lineError = "Error in {0}."
lineErrorLink = "line {0}"
restartButton = "Restart"
title = "Startup Error"
+
+[vehicleSettings]
+cancel = "Cancel"
+template = "Manufacturer"
+test = "Test"
diff --git a/package-lock.json b/package-lock.json
index 43be5edf4..ab588b1b5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,7 @@
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-vue": "^9.3.0",
"histoire": "^0.12.4",
+ "json-to-pretty-yaml": "^1.2.2",
"markty-toml": "^0.1.1",
"prettier": "^2.5.1",
"prettier-plugin-toml": "^0.3.1",
@@ -3816,6 +3817,18 @@
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
},
+ "node_modules/json-to-pretty-yaml": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz",
+ "integrity": "sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==",
+ "dependencies": {
+ "remedial": "^1.0.7",
+ "remove-trailing-spaces": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.2.0"
+ }
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -4640,6 +4653,19 @@
"url": "https://github.com/sponsors/mysticatea"
}
},
+ "node_modules/remedial": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz",
+ "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/remove-trailing-spaces": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz",
+ "integrity": "sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA=="
+ },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -8657,6 +8683,15 @@
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
},
+ "json-to-pretty-yaml": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz",
+ "integrity": "sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==",
+ "requires": {
+ "remedial": "^1.0.7",
+ "remove-trailing-spaces": "^1.0.6"
+ }
+ },
"json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -9260,6 +9295,16 @@
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg=="
},
+ "remedial": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz",
+ "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg=="
+ },
+ "remove-trailing-spaces": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz",
+ "integrity": "sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA=="
+ },
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
diff --git a/package.json b/package.json
index c9aef901f..5158a6e45 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-vue": "^9.3.0",
"histoire": "^0.12.4",
+ "json-to-pretty-yaml": "^1.2.2",
"markty-toml": "^0.1.1",
"prettier": "^2.5.1",
"prettier-plugin-toml": "^0.3.1",
diff --git a/server/http.go b/server/http.go
index 685b8da5a..fc631d8b3 100644
--- a/server/http.go
+++ b/server/http.go
@@ -97,6 +97,8 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, cache *util.Cache) {
routes := map[string]route{
"health": {[]string{"GET"}, "/health", healthHandler(site)},
"state": {[]string{"GET"}, "/state", stateHandler(cache)},
+ "config": {[]string{"GET"}, "/config/templates/{class:[a-z]+}", templatesHandler},
+ "test": {[]string{"POST"}, "/config/test/{class:[a-z]+}", testHandler},
"buffersoc": {[]string{"POST", "OPTIONS"}, "/buffersoc/{value:[0-9.]+}", floatHandler(site.SetBufferSoc, site.GetBufferSoc)},
"prioritysoc": {[]string{"POST", "OPTIONS"}, "/prioritysoc/{value:[0-9.]+}", floatHandler(site.SetPrioritySoc, site.GetPrioritySoc)},
"residualpower": {[]string{"POST", "OPTIONS"}, "/residualpower/{value:[-0-9.]+}", floatHandler(site.SetResidualPower, site.GetResidualPower)},
diff --git a/server/http_config_handler.go b/server/http_config_handler.go
new file mode 100644
index 000000000..898fd961a
--- /dev/null
+++ b/server/http_config_handler.go
@@ -0,0 +1,118 @@
+package server
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/evcc-io/evcc/api"
+ "github.com/evcc-io/evcc/charger"
+ "github.com/evcc-io/evcc/meter"
+ "github.com/evcc-io/evcc/util/templates"
+ "github.com/evcc-io/evcc/vehicle"
+ "github.com/gorilla/mux"
+)
+
+// templatesHandler returns the list of templates by class
+func templatesHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+
+ class, err := templates.ClassString(vars["class"])
+ if err != nil {
+ jsonError(w, http.StatusBadRequest, err)
+ return
+ }
+
+ res := templates.ByClass(class)
+ jsonResult(w, res)
+}
+
+// testHandler tests a configuration by class
+func testHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+
+ class, err := templates.ClassString(vars["class"])
+ if err != nil {
+ jsonError(w, http.StatusBadRequest, err)
+ return
+ }
+
+ var req map[string]any
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ jsonError(w, http.StatusBadRequest, err)
+ return
+ }
+
+ // var req struct {
+ // Type string
+ // Other map[string]any `mapstructure:",remain"`
+ // }
+
+ // if err := mapstructure.Decode(plain, &req); err != nil {
+ // jsonError(w, http.StatusBadRequest, err)
+ // return
+ // }
+
+ // tmpl, err := templates.ByName(class, req.Name)
+ // if err != nil {
+ // jsonError(w, http.StatusBadRequest, err)
+ // return
+ // }
+
+ // b, _, err := tmpl.RenderResult(templates.TemplateRenderModeInstance, req.Other)
+ // if err != nil {
+ // jsonError(w, http.StatusBadRequest, err)
+ // return
+ // }
+
+ // var instance any
+ // if err := yaml.Unmarshal(b, &instance); err != nil {
+ // jsonError(w, http.StatusBadRequest, err)
+ // return
+ // }
+
+ typ := "template"
+ if req["type"] != nil {
+ typ = req["type"].(string)
+ delete(req, "type")
+ }
+
+ var dev any
+
+ switch class {
+ case templates.Charger:
+ dev, err = charger.NewFromConfig(typ, req)
+ case templates.Meter:
+ dev, err = meter.NewFromConfig(typ, req)
+ case templates.Vehicle:
+ dev, err = vehicle.NewFromConfig(typ, req)
+ }
+
+ if err != nil {
+ jsonError(w, http.StatusBadRequest, err)
+ return
+ }
+
+ type result = struct {
+ Value any `json:"value"`
+ Error error `json:"error"`
+ }
+
+ res := make(map[string]result)
+
+ if dev, ok := dev.(api.Meter); ok {
+ val, err := dev.CurrentPower()
+ res["CurrentPower"] = result{val, err}
+ }
+
+ if dev, ok := dev.(api.MeterEnergy); ok {
+ val, err := dev.TotalEnergy()
+ res["TotalEnergy"] = result{val, err}
+ }
+
+ if dev, ok := dev.(api.Battery); ok {
+ val, err := dev.Soc()
+ res["Soc"] = result{val, err}
+ }
+
+ jsonResult(w, res)
+}
diff --git a/templates/definition/charger/homematic.yaml b/templates/definition/charger/homematic.yaml
index 6626533ca..a21a87727 100644
--- a/templates/definition/charger/homematic.yaml
+++ b/templates/definition/charger/homematic.yaml
@@ -28,7 +28,7 @@ params:
mask: true
- name: meterchannel
default: 6
- valuetype: int
+ type: int
required: true
advanced: true
description:
@@ -39,7 +39,7 @@ params:
de: Kanalnummer des Messwertkanals, wie im CCU Webfrontend angezeigt.
- name: switchchannel
default: 3
- valuetype: int
+ type: int
required: true
advanced: true
description:
diff --git a/templates/definition/charger/ocpp.yaml b/templates/definition/charger/ocpp.yaml
index fd33c00b9..26b7807bd 100644
--- a/templates/definition/charger/ocpp.yaml
+++ b/templates/definition/charger/ocpp.yaml
@@ -36,7 +36,7 @@ requirements:
* Local network connection
params:
- name: stationid
- valuetype: string
+ type: string
advanced: true
help:
en: The chargers unique station id (or chargepoint id)
@@ -44,19 +44,19 @@ params:
example: EVB-P12354
- name: connector
default: 1
- valuetype: int
+ type: int
advanced: true
help:
en: Connector number, usually 1 for first connector.
de: Verwendeter Ladepunkt, normalerweise 1 für den ersten Anschluss.
- name: meter
default: false
- valuetype: bool
+ type: bool
help:
en: Use integrated meter (if present)
de: Benutze den integrierten Zähler (falls vorhanden)
- name: idtag
- valuetype: string
+ type: string
advanced: true
help:
en: Token-ID returned to the charger for authorisation of charging sessions
@@ -65,7 +65,7 @@ params:
- name: timeout
advanced: true
default: 10m
- valuetype: duration
+ type: duration
render: |
type: ocpp
{{- if ne .stationid "" }}
diff --git a/templates/definition/charger/openwb.yaml b/templates/definition/charger/openwb.yaml
index 58b106a3b..95b36f898 100644
--- a/templates/definition/charger/openwb.yaml
+++ b/templates/definition/charger/openwb.yaml
@@ -12,7 +12,7 @@ params:
- name: host
- name: connector
- name: phases1p3p
- valuetype: bool
+ type: bool
description:
en: Charger is equipped with phase switching feature
de: Phasenumschaltung vorhanden
diff --git a/templates/definition/charger/tinkerforge-warp.yaml b/templates/definition/charger/tinkerforge-warp.yaml
index 3c902b57b..34a4e7828 100644
--- a/templates/definition/charger/tinkerforge-warp.yaml
+++ b/templates/definition/charger/tinkerforge-warp.yaml
@@ -17,7 +17,7 @@ requirements:
params:
- name: fw2
required: true
- valuetype: bool
+ type: bool
help:
de: Firmware v2 installiert
en: Firmware v2 installed
@@ -39,7 +39,7 @@ params:
- name: timeout
advanced: true
default: 30s
- valuetype: duration
+ type: duration
help:
de: Akzeptiere keine Daten die älter sind als dieser Wert
en: Don't accept values older than this value
diff --git a/templates/definition/defaults.yaml b/templates/definition/defaults.yaml
index 190441cd2..05f55986f 100644
--- a/templates/definition/defaults.yaml
+++ b/templates/definition/defaults.yaml
@@ -10,7 +10,7 @@ params:
description:
generic: Modbus ID
default: 1
- valuetype: number
+ type: number
- name: modbusdevice
description:
de: Geräteadresse
@@ -27,7 +27,7 @@ params:
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
+ type: number
- name: modbuscomset
description:
de: ComSet
@@ -60,7 +60,7 @@ params:
help:
de: Port
en: Port
- valuetype: number
+ type: number
- name: interface
description:
de: Netzwerkinterface
@@ -100,7 +100,7 @@ params:
de: Akkukapazität in kWh
en: Battery capacity in kWh
example: "50"
- valuetype: float
+ type: float
usages: ["vehicle", "battery"]
- name: vin
description:
@@ -118,7 +118,7 @@ params:
de: Die maximale Anzahl der Phasen welche genutzt werden können
en: The maximum number of phases which can be used
example: 3
- valuetype: number
+ type: number
- name: connector
description:
de: Ladepunkt (falls >1 Ladepunkt)
@@ -133,7 +133,7 @@ params:
de: Zeitintervall nach dem Daten erneut vom Fahrzeug abgefragt werden
en: Time interval with when data should be reloaded from the vehicle
advanced: true
- valuetype: duration
+ type: duration
example: 5m
- name: cloud
description:
@@ -158,14 +158,14 @@ params:
- No waiting for a new evcc version in this case and then requiring an update
requirements:
evcc: ["sponsorship"]
- valuetype: bool
+ type: bool
- name: timeout
description:
de: Zeitüberschreitung
en: Timeout
advanced: true
example: 10s
- valuetype: duration
+ type: duration
- name: mode
description:
de: Standard-Lademodus, wenn ein Fahrzeug angeschlossen ist
@@ -173,7 +173,7 @@ params:
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
+ type: chargemodes
- name: minsoc
description:
de: Minimaler Ladestand (Soc) in %
@@ -182,7 +182,7 @@ params:
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
+ type: number
- name: targetsoc
description:
de: Ziel-Ladestand (Soc) in %
@@ -191,7 +191,7 @@ params:
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
+ type: number
- name: mincurrent
description:
de: Minimale Stromstärke in Ampere (A)
@@ -200,7 +200,7 @@ params:
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
+ type: number
- name: maxcurrent
description:
de: Maximale Stromstärke in Ampere (A)
@@ -209,7 +209,7 @@ params:
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
+ type: number
- name: identifiers
description:
de: Identifikation
@@ -217,7 +217,7 @@ params:
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
+ type: stringlist
- name: standbypower
description:
de: Standby-Leistung in W
@@ -225,7 +225,7 @@ params:
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
+ type: number
- name: language
description:
de: Sprache
@@ -282,7 +282,7 @@ presets:
- name: cache
default: 15m
advanced: true
- valuetype: duration
+ type: duration
render: |
{{define "vehicle-base"}}
{{- if ne .title "" }}
@@ -320,7 +320,7 @@ presets:
advanced: true
- name: identifiers
advanced: true
- valuetype: stringlist
+ type: stringlist
render: |
{{define "vehicle-identify"}}
{{- if or (ne .mode "") (ne .minSoc "") (ne .targetSoc "") (ne .minCurrent "") (ne .maxCurrent "") }}
diff --git a/templates/definition/meter/dsmr.yaml b/templates/definition/meter/dsmr.yaml
index 63202be18..ff60c547b 100644
--- a/templates/definition/meter/dsmr.yaml
+++ b/templates/definition/meter/dsmr.yaml
@@ -15,7 +15,7 @@ params:
de: Typischerweise 1-0:1.8.0, bei Mehrtarifzählern 1-0:1.8.1 oder 1-0:1.8.2
en: Typically 1-0:1.8.0 or 1-0:1.8.1/1-0:1.8.2 with multiple tariffs
advanced: true
- valuetype: string
+ type: string
render: |
type: dsmr
uri: {{ .host }}:{{ .port }}
diff --git a/templates/definition/meter/homematic.yaml b/templates/definition/meter/homematic.yaml
index ef3beedf5..edaf1b1ee 100644
--- a/templates/definition/meter/homematic.yaml
+++ b/templates/definition/meter/homematic.yaml
@@ -23,7 +23,7 @@ params:
mask: true
- name: meterchannel
default: 6
- valuetype: int
+ type: int
required: true
advanced: true
description:
diff --git a/templates/definition/meter/huawei-sun2000-dongle-powersensor.yaml b/templates/definition/meter/huawei-sun2000-dongle-powersensor.yaml
index 9bc35d7c2..430ee3cf2 100644
--- a/templates/definition/meter/huawei-sun2000-dongle-powersensor.yaml
+++ b/templates/definition/meter/huawei-sun2000-dongle-powersensor.yaml
@@ -8,7 +8,7 @@ params:
choice: ["grid", "pv", "battery"]
allinone: true
- name: storageunit
- valuetype: number
+ type: number
default: 1
advanced: true
- name: modbus
diff --git a/templates/definition/meter/solaredge-hybrid.yaml b/templates/definition/meter/solaredge-hybrid.yaml
index d607aa362..8f831e260 100644
--- a/templates/definition/meter/solaredge-hybrid.yaml
+++ b/templates/definition/meter/solaredge-hybrid.yaml
@@ -16,7 +16,7 @@ params:
id: 1
port: 1502
- name: timeout
- valuetype: duration
+ type: duration
- name: capacity
advanced: true
render: |
diff --git a/templates/definition/meter/solaredge-inverter.yaml b/templates/definition/meter/solaredge-inverter.yaml
index cf9c158b5..0fb6abf76 100644
--- a/templates/definition/meter/solaredge-inverter.yaml
+++ b/templates/definition/meter/solaredge-inverter.yaml
@@ -17,7 +17,7 @@ params:
id: 1
port: 1502
- name: timeout
- valuetype: duration
+ type: duration
render: |
type: custom
power:
diff --git a/templates/definition/meter/sunspec-hybrid.yaml b/templates/definition/meter/sunspec-hybrid.yaml
index d37aeba80..78ca3ea33 100644
--- a/templates/definition/meter/sunspec-hybrid.yaml
+++ b/templates/definition/meter/sunspec-hybrid.yaml
@@ -19,7 +19,7 @@ params:
de: Einstellung Float/Integer im Wechselrichter überprüfen
en: Verify Float/Integer setting in inverter
advanced: true
- valuetype: bool
+ type: bool
- name: capacity
advanced: true
render: |
diff --git a/templates/definition/meter/sunspec-inverter.yaml b/templates/definition/meter/sunspec-inverter.yaml
index 76ce7ba2f..c86d31fdc 100644
--- a/templates/definition/meter/sunspec-inverter.yaml
+++ b/templates/definition/meter/sunspec-inverter.yaml
@@ -18,7 +18,7 @@ params:
de: Einstellung Float/Integer im Wechselrichter überprüfen
en: Verify Float/Integer setting in inverter
advanced: true
- valuetype: bool
+ type: bool
render: |
type: modbus
{{- include "modbus" . }}
diff --git a/templates/definition/vehicle/evnotify.yaml b/templates/definition/vehicle/evnotify.yaml
index 5382cda75..e731af8fe 100644
--- a/templates/definition/vehicle/evnotify.yaml
+++ b/templates/definition/vehicle/evnotify.yaml
@@ -10,7 +10,7 @@ params:
- name: token
required: true
- name: capacity
- valuetype: float
+ type: float
- name: phases
advanced: true
- name: icon
diff --git a/templates/definition/vehicle/offline.yaml b/templates/definition/vehicle/offline.yaml
index 6bba62a0a..2ed541f59 100644
--- a/templates/definition/vehicle/offline.yaml
+++ b/templates/definition/vehicle/offline.yaml
@@ -19,7 +19,7 @@ params:
advanced: true
- name: identifiers
advanced: true
- valuetype: stringlist
+ type: stringlist
render: |
type: custom
features: ["offline"]
diff --git a/util/templates/class.go b/util/templates/class.go
new file mode 100644
index 000000000..d88ee114a
--- /dev/null
+++ b/util/templates/class.go
@@ -0,0 +1,19 @@
+package templates
+
+type Class int
+
+func (c *Class) UnmarshalText(text []byte) error {
+ class, err := ClassString(string(text))
+ if err == nil {
+ *c = class
+ }
+ return err
+}
+
+//go:generate enumer -type Class
+const (
+ _ Class = iota
+ Charger
+ Meter
+ Vehicle
+)
diff --git a/util/templates/class_enumer.go b/util/templates/class_enumer.go
new file mode 100644
index 000000000..bea859f01
--- /dev/null
+++ b/util/templates/class_enumer.go
@@ -0,0 +1,83 @@
+// Code generated by "enumer -type Class"; DO NOT EDIT.
+
+package templates
+
+import (
+ "fmt"
+ "strings"
+)
+
+const _ClassName = "ChargerMeterVehicle"
+
+var _ClassIndex = [...]uint8{0, 7, 12, 19}
+
+const _ClassLowerName = "chargermetervehicle"
+
+func (i Class) String() string {
+ i -= 1
+ if i < 0 || i >= Class(len(_ClassIndex)-1) {
+ return fmt.Sprintf("Class(%d)", i+1)
+ }
+ return _ClassName[_ClassIndex[i]:_ClassIndex[i+1]]
+}
+
+// An "invalid array index" compiler error signifies that the constant values have changed.
+// Re-run the stringer command to generate them again.
+func _ClassNoOp() {
+ var x [1]struct{}
+ _ = x[Charger-(1)]
+ _ = x[Meter-(2)]
+ _ = x[Vehicle-(3)]
+}
+
+var _ClassValues = []Class{Charger, Meter, Vehicle}
+
+var _ClassNameToValueMap = map[string]Class{
+ _ClassName[0:7]: Charger,
+ _ClassLowerName[0:7]: Charger,
+ _ClassName[7:12]: Meter,
+ _ClassLowerName[7:12]: Meter,
+ _ClassName[12:19]: Vehicle,
+ _ClassLowerName[12:19]: Vehicle,
+}
+
+var _ClassNames = []string{
+ _ClassName[0:7],
+ _ClassName[7:12],
+ _ClassName[12:19],
+}
+
+// ClassString retrieves an enum value from the enum constants string name.
+// Throws an error if the param is not part of the enum.
+func ClassString(s string) (Class, error) {
+ if val, ok := _ClassNameToValueMap[s]; ok {
+ return val, nil
+ }
+
+ if val, ok := _ClassNameToValueMap[strings.ToLower(s)]; ok {
+ return val, nil
+ }
+ return 0, fmt.Errorf("%s does not belong to Class values", s)
+}
+
+// ClassValues returns all values of the enum
+func ClassValues() []Class {
+ return _ClassValues
+}
+
+// ClassStrings returns a slice of all String values of the enum
+func ClassStrings() []string {
+ strs := make([]string, len(_ClassNames))
+ copy(strs, _ClassNames)
+ return strs
+}
+
+// IsAClass returns "true" if the value is listed in the enum definition. "false" otherwise
+func (i Class) IsAClass() bool {
+ for _, v := range _ClassValues {
+ if i == v {
+ return true
+ }
+ }
+ return false
+}
diff --git a/util/templates/config.go b/util/templates/config.go
index 455b7281f..0d8bb7b43 100644
--- a/util/templates/config.go
+++ b/util/templates/config.go
@@ -41,8 +41,8 @@ func (c *ConfigDefaults) LoadDefaults() {
if p.IsReference() {
finalName := p.Name
referencedItemName := p.Name
- if p.Referencename != "" {
- referencedItemName = p.Referencename
+ if p.ReferenceName != "" {
+ referencedItemName = p.ReferenceName
}
_, referencedParam := c.ParamByName(referencedItemName)
referencedParam.OverwriteProperties(p)
diff --git a/util/templates/generate/generate.go b/util/templates/generate/generate.go
index 69aceaf68..f6936b45b 100644
--- a/util/templates/generate/generate.go
+++ b/util/templates/generate/generate.go
@@ -65,7 +65,7 @@ func writeTemplate(class templates.Class, index int, product templates.Product,
b, err := tmpl.RenderDocumentation(product, values, "de")
if err == nil {
- filename := fmt.Sprintf("%s/%s/%s_%d.yaml", docsPath, class, tmpl.Template, index)
+ filename := fmt.Sprintf("%s/%s/%s_%d.yaml", docsPath, strings.ToLower(class.String()), tmpl.Template, index)
err = os.WriteFile(filename, b, 0o644)
}
diff --git a/util/templates/init.go b/util/templates/init.go
index 2ad93feb1..8a0fac2d5 100644
--- a/util/templates/init.go
+++ b/util/templates/init.go
@@ -16,14 +16,6 @@ var (
configDefaults = ConfigDefaults{}
)
-type Class string
-
-const (
- Charger Class = "charger"
- Meter Class = "meter"
- Vehicle Class = "vehicle"
-)
-
func init() {
configDefaults.LoadDefaults()
@@ -58,6 +50,13 @@ func FromBytes(b []byte) (Template, error) {
err = tmpl.Validate()
}
+ // set default value type
+ for _, p := range tmpl.Params {
+ if p.Type == "" {
+ p.Type = ParamTypeString
+ }
+ }
+
return tmpl, err
}
@@ -84,8 +83,12 @@ func loadTemplates(class Class) {
return fmt.Errorf("processing template '%s' failed: %w", filepath, err)
}
- path := Class(path.Dir(filepath))
- templates[path] = append(templates[path], tmpl)
+ class, err := ClassString(path.Dir(filepath))
+ if err != nil {
+ return fmt.Errorf("invalid template class: '%s'", err)
+ }
+
+ templates[class] = append(templates[class], tmpl)
return nil
})
diff --git a/util/templates/template.go b/util/templates/template.go
index 607f75a21..cae4f05c1 100644
--- a/util/templates/template.go
+++ b/util/templates/template.go
@@ -16,7 +16,7 @@ import (
type Template struct {
TemplateDefinition
- ConfigDefaults ConfigDefaults
+ ConfigDefaults ConfigDefaults `json:"-"`
Lang string
@@ -33,8 +33,8 @@ func (t *Template) GuidedSetupEnabled() bool {
// UpdateParamWithDefaults adds default values to specific param name entries
func (t *Template) UpdateParamsWithDefaults() error {
for i, p := range t.Params {
- if p.ValueType == "" || (p.ValueType != "" && !slices.Contains(ValidParamValueTypes, p.ValueType)) {
- t.Params[i].ValueType = ParamValueTypeString
+ if p.Type == "" || (p.Type != "" && !slices.Contains(ValidParamTypes, p.Type)) {
+ t.Params[i].Type = ParamTypeString
}
if index, resultMapItem := t.ConfigDefaults.ParamByName(strings.ToLower(p.Name)); index > -1 {
@@ -75,8 +75,8 @@ func (t *Template) Validate() error {
}
}
- if p.ValueType != "" && !slices.Contains(ValidParamValueTypes, p.ValueType) {
- return fmt.Errorf("invalid value type '%s' in template %s", p.ValueType, t.Template)
+ if p.Type != "" && !slices.Contains(ValidParamTypes, p.Type) {
+ return fmt.Errorf("invalid value type '%s' in template %s", p.Type, t.Template)
}
}
@@ -234,8 +234,8 @@ func (t *Template) RenderProxyWithValues(values map[string]interface{}, lang str
continue
}
- switch p.ValueType {
- case ParamValueTypeStringList:
+ switch p.Type {
+ case ParamTypeStringList:
for _, e := range v.([]string) {
t.Params[index].Values = append(p.Values, yamlQuote(e))
}
@@ -254,8 +254,8 @@ func (t *Template) RenderProxyWithValues(values map[string]interface{}, lang str
var newParams []Param
for _, param := range t.Params {
if !param.IsRequired() {
- switch param.ValueType {
- case ParamValueTypeStringList:
+ switch param.Type {
+ case ParamTypeStringList:
if len(param.Values) == 0 {
continue
}
diff --git a/util/templates/template_documentation.go b/util/templates/template_documentation.go
index 572b5f4c3..5996dd0ae 100644
--- a/util/templates/template_documentation.go
+++ b/util/templates/template_documentation.go
@@ -24,8 +24,8 @@ func (t *Template) RenderDocumentation(product Product, values map[string]interf
continue
}
- switch p.ValueType {
- case ParamValueTypeStringList:
+ switch p.Type {
+ case ParamTypeStringList:
for _, e := range v.([]string) {
t.Params[index].Values = append(p.Values, yamlQuote(e))
}
diff --git a/util/templates/template_types.go b/util/templates/template_types.go
index db8f05df9..9df7b426c 100644
--- a/util/templates/template_types.go
+++ b/util/templates/template_types.go
@@ -40,16 +40,16 @@ const (
)
const (
- ParamValueTypeString = "string"
- ParamValueTypeNumber = "number"
- ParamValueTypeFloat = "float"
- ParamValueTypeBool = "bool"
- ParamValueTypeStringList = "stringlist"
- ParamValueTypeChargeModes = "chargemodes"
- ParamValueTypeDuration = "duration"
+ ParamTypeString = "string"
+ ParamTypeNumber = "number"
+ ParamTypeFloat = "float"
+ ParamTypeBool = "bool"
+ ParamTypeStringList = "stringlist"
+ ParamTypeChargeModes = "chargemodes"
+ ParamTypeDuration = "duration"
)
-var ValidParamValueTypes = []string{ParamValueTypeString, ParamValueTypeNumber, ParamValueTypeFloat, ParamValueTypeBool, ParamValueTypeStringList, ParamValueTypeChargeModes, ParamValueTypeDuration}
+var ValidParamTypes = []string{ParamTypeString, ParamTypeNumber, ParamTypeFloat, ParamTypeBool, ParamTypeStringList, ParamTypeChargeModes, ParamTypeDuration}
var (
ValidModbusChoices = []string{ModbusChoiceRS485, ModbusChoiceTCPIP}
@@ -91,9 +91,9 @@ var predefinedTemplateProperties = []string{
// TextLanguage contains language-specific texts
type TextLanguage struct {
- Generic string // language independent
- DE string // german text
- EN string // english text
+ Generic string `json:",omitempty"` // language independent
+ DE string `json:",omitempty"` // german text
+ EN string `json:",omitempty"` // english text
}
func (t *TextLanguage) String(lang string) string {
@@ -166,44 +166,45 @@ type LinkedTemplate struct {
// 2. in defaults.yaml presets: can ne referenced in 1 and some values set here can be overwritten in 1. See OverwriteProperties method
// 3. in defaults.yaml modbus section: are referenced in 1 by a `name:modbus` param entry. Some values here can be overwritten in 1.
// 4. in defaults.yaml param section: defaults for some params
-// Generelle Reihenfolge der Werte (außer Description, Default, ValueType):
+// Generelle Reihenfolge der Werte (außer Description, Default, Type):
// 1. defaults.yaml param section
// 2. defaults.yaml presets
// 3. defaults.yaml modbus section
// 4. template
type Param struct {
- Reference *bool // if this is references another param definition
- Referencename string // name of the referenced param if it is not identical to the defined name
- Preset string // Reference a predefined set of params
Name string // Param name which is used for assigning defaults properties and referencing in render
Description TextLanguage // language specific titles (presented in UI instead of Name)
- Required *bool // cli if the user has to provide a non empty value
- Mask *bool // cli if the value should be masked, e.g. for passwords
- Advanced *bool // cli if the user does not need to be asked. Requires a "Default" to be defined.
- Hidden *bool // cli if the parameter should not be presented in the cli, the default value be assigned
- Deprecated *bool // if the parameter is deprecated and thus should not be presented in the cli or docs
- Default string // default value if no user value is provided in the configuration
- Example string // cli example value
Help TextLanguage // cli configuration help
- Value string // user provided value via cli configuration
- Values []string // user provided list of values e.g. for ValueType "stringlist"
- Usages []string // restrict param to these usage types, e.g. "battery" for home battery capacity
- ValueType string // string representation of the value type, "string" is default
- ValidValues []string // list of valid values the user can provide
- Choice []string // defines a set of choices, e.g. "grid", "pv", "battery", "charge" for "usage"
- AllInOne *bool // defines if the defined usages can all be present in a single device
- Requirements Requirements // requirements for this param to be usable, only supported via ValueType "bool"
+ Reference *bool `json:",omitempty"` // if this is references another param definition
+ ReferenceName string `json:",omitempty"` // name of the referenced param if it is not identical to the defined name
+ Preset string `json:"-"` // Reference a predefined set of params
+ Required *bool `json:",omitempty"` // cli if the user has to provide a non empty value
+ Mask *bool `json:",omitempty"` // cli if the value should be masked, e.g. for passwords
+ Advanced *bool `json:",omitempty"` // cli if the user does not need to be asked. Requires a "Default" to be defined.
+ Hidden *bool `json:",omitempty"` // cli if the parameter should not be presented in the cli, the default value be assigned
+ Deprecated *bool `json:",omitempty"` // if the parameter is deprecated and thus should not be presented in the cli or docs
+ Default string `json:",omitempty"` // default value if no user value is provided in the configuration
+ Example string `json:",omitempty"` // cli example value
+ Value string `json:"-"` // user provided value via cli configuration
+ Values []string `json:",omitempty"` // user provided list of values e.g. for Type "stringlist"
+ Usages []string `json:",omitempty"` // restrict param to these usage types, e.g. "battery" for home battery capacity
+ Type string // string representation of the value type, "string" is default
+ ValidValues []string `json:",omitempty"` // list of valid values the user can provide
+ Choice []string `json:",omitempty"` // defines a set of choices, e.g. "grid", "pv", "battery", "charge" for "usage"
+ AllInOne *bool `json:"-"` // defines if the defined usages can all be present in a single device
+ Requirements Requirements `json:"-"` // requirements for this param to be usable, only supported via Type "bool"
- Baudrate int // device specific default for modbus RS485 baudrate
- Comset string // device specific default for modbus RS485 comset
- Port int // device specific default for modbus TCPIP port
- ID int // device specific default for modbus ID
+ // TODO move somewhere else should not be part of the param definition
+ Baudrate int `json:"-"` // device specific default for modbus RS485 baudrate
+ Comset string `json:"-"` // device specific default for modbus RS485 comset
+ Port int `json:"-"` // device specific default for modbus TCPIP port
+ ID int `json:"-"` // device specific default for modbus ID
}
// return a default value or example value depending on the renderMode
func (p *Param) DefaultValue(renderMode string) interface{} {
// return empty list to allow iterating over in template
- if p.ValueType == ParamValueTypeStringList {
+ if p.Type == ParamTypeStringList {
return []string{}
}
@@ -252,7 +253,7 @@ func (p *Param) IsAllInOne() bool {
// Product contains naming information about a product a template supports
type Product struct {
Brand string // product brand
- Description TextLanguage // product name
+ Description TextLanguage `json:",omitempty"` // product name
}
func (p Product) Title(lang string) string {
@@ -262,12 +263,12 @@ func (p Product) Title(lang string) string {
// TemplateDefinition contains properties of a device template
type TemplateDefinition struct {
Template string
- Group string // the group this template belongs to, references groupList entries
- Covers []string // list of covered outdated template names
- Products []Product // list of products this template is compatible with
- Capabilities []string
- Requirements Requirements
- Linked []LinkedTemplate // a list of templates that should be processed as part of the guided setup
- Params []Param
- Render string // rendering template
+ Group string `json:",omitempty"` // the group this template belongs to, references groupList entries
+ Covers []string `json:",omitempty"` // list of covered outdated template names
+ Products []Product `json:",omitempty"` // list of products this template is compatible with
+ Capabilities []string `json:",omitempty"`
+ Requirements Requirements `json:"-"`
+ Linked []LinkedTemplate `json:",omitempty"` // a list of templates that should be processed as part of the guided setup
+ Params []Param `json:",omitempty"`
+ Render string `json:"-"` // rendering template
}