From a4ed1080e1f2594270f22f7e9570bc75d1e219c6 Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Sun, 1 Mar 2026 12:06:34 +0100 Subject: [PATCH] Config UI: add tariffs (#26698) Co-authored-by: Maschga <88616799+Maschga@users.noreply.github.com> Co-authored-by: andig Co-authored-by: andig --- api/globalconfig/types.go | 29 ++ assets/css/app.css | 3 + .../ChargingPlans/PlanRepeatingSettings.vue | 2 +- assets/js/components/Config/CurrencyModal.vue | 121 ++++++++ .../components/Config/DeviceModal/Actions.vue | 3 + .../Config/DeviceModal/DeviceModalBase.vue | 10 +- assets/js/components/Config/DeviceTags.vue | 219 ++++++++++---- assets/js/components/Config/GeneralConfig.vue | 14 +- assets/js/components/Config/PropertyEntry.vue | 2 + assets/js/components/Config/PropertyField.vue | 36 ++- .../js/components/Config/PropertyZoneForm.vue | 232 +++++++++++++++ .../components/Config/PropertyZoneSummary.vue | 69 +++++ .../components/Config/PropertyZonesField.vue | 110 +++++++ assets/js/components/Config/TariffCard.vue | 76 +++++ assets/js/components/Config/TariffModal.vue | 255 ++++++++++++++++ ...ariffsModal.vue => TariffsLegacyModal.vue} | 17 +- assets/js/components/Config/TestResult.vue | 8 +- .../Config/defaultYaml/tariffCo2.yaml | 28 ++ .../Config/defaultYaml/tariffPrice.yaml | 25 ++ .../Config/defaultYaml/tariffSolar.yaml | 28 ++ .../js/components/Tariff/SmartTariffBase.vue | 56 +--- assets/js/mixins/formatter.test.ts | 91 +++++- assets/js/mixins/formatter.ts | 163 +++++++---- assets/js/mixins/zoneUtils.ts | 64 ++++ assets/js/types/evcc.ts | 23 +- assets/js/utils/tariffSlots.test.ts | 82 ++++++ assets/js/utils/tariffSlots.ts | 64 ++++ assets/js/views/Config.vue | 254 ++++++++++++---- cmd/refs.go | 36 ++- cmd/root.go | 3 + cmd/setup.go | 140 +++++++-- core/keys/global.go | 1 + i18n/de.json | 83 +++++- i18n/en.json | 84 +++++- package.json | 1 + server/http.go | 9 + server/http_config_device_handler.go | 52 ++++ server/http_config_helper.go | 31 ++ server/http_config_tariff_handler.go | 74 +++++ .../definition/tariff/demo-dynamic-grid.yaml | 4 +- .../definition/tariff/energy-charts-api.yaml | 9 +- templates/definition/tariff/ews.yaml | 4 +- templates/definition/tariff/fixed-zones.yaml | 38 +++ templates/definition/tariff/fixed.yaml | 16 + templates/definition/tariff/octopus-de.yaml | 5 +- tests/config-aux.spec.ts | 3 - tests/config-tariffs.spec.ts | 276 +++++++++++++++++- tests/plan-fixed-tariff.evcc.yaml | 5 + tests/tariffs-legacy.sql | 8 + util/config/instance.go | 6 + util/templates/defaults.yaml | 11 +- util/templates/paramtype.go | 2 + util/templates/paramtype_enumer.go | 16 +- util/templates/template.go | 17 +- util/templates/types.go | 3 + 55 files changed, 2711 insertions(+), 310 deletions(-) create mode 100644 assets/js/components/Config/CurrencyModal.vue create mode 100644 assets/js/components/Config/PropertyZoneForm.vue create mode 100644 assets/js/components/Config/PropertyZoneSummary.vue create mode 100644 assets/js/components/Config/PropertyZonesField.vue create mode 100644 assets/js/components/Config/TariffCard.vue create mode 100644 assets/js/components/Config/TariffModal.vue rename assets/js/components/Config/{TariffsModal.vue => TariffsLegacyModal.vue} (52%) create mode 100644 assets/js/components/Config/defaultYaml/tariffCo2.yaml create mode 100644 assets/js/components/Config/defaultYaml/tariffPrice.yaml create mode 100644 assets/js/components/Config/defaultYaml/tariffSolar.yaml create mode 100644 assets/js/mixins/zoneUtils.ts create mode 100644 assets/js/utils/tariffSlots.test.ts create mode 100644 assets/js/utils/tariffSlots.ts create mode 100644 server/http_config_tariff_handler.go create mode 100644 templates/definition/tariff/fixed-zones.yaml create mode 100644 templates/definition/tariff/fixed.yaml create mode 100644 tests/tariffs-legacy.sql diff --git a/api/globalconfig/types.go b/api/globalconfig/types.go index ab7fda8a8..4c91edcc5 100644 --- a/api/globalconfig/types.go +++ b/api/globalconfig/types.go @@ -2,6 +2,7 @@ package globalconfig import ( "encoding/json" + "iter" "net" "os" "strconv" @@ -173,6 +174,34 @@ type Tariffs struct { Solar []config.Typed } +func (c Tariffs) IsConfigured() bool { + return c.Currency != "" || c.Grid.Type != "" || c.FeedIn.Type != "" || c.Co2.Type != "" || c.Planner.Type != "" || len(c.Solar) > 0 +} + +type TariffRefs struct { + Grid string `json:"grid"` + FeedIn string `json:"feedIn"` + Co2 string `json:"co2"` + Planner string `json:"planner"` + Solar []string `json:"solar"` +} + +func (refs TariffRefs) IsConfigured() bool { + return refs.Grid != "" || refs.FeedIn != "" || refs.Co2 != "" || refs.Planner != "" || len(refs.Solar) > 0 +} + +func (refs TariffRefs) Used() iter.Seq[string] { + return func(yield func(string) bool) { + for _, ref := range append([]string{refs.Grid, refs.FeedIn, refs.Co2, refs.Planner}, refs.Solar...) { + if ref != "" { + if !yield(ref) { + return + } + } + } + } +} + type Network struct { Schema_ string `json:"schema,omitempty" mapstructure:"schema"` // TODO deprecated ExternalUrl string `json:"externalUrl"` diff --git a/assets/css/app.css b/assets/css/app.css index d450fc91a..6ca11349b 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -72,6 +72,9 @@ --bs-warning: var(--evcc-orange); --bs-warning-rgb: var(--evcc-orange-rgb); + --bs-warning-bg-subtle: color-mix(in srgb, var(--evcc-orange) 10%, transparent); + --bs-warning-border-subtle: color-mix(in srgb, var(--evcc-orange) 30%, transparent); + --bs-warning-text-emphasis: var(--evcc-orange); --bs-danger-50: #dc354580; --bs-danger: var(--evcc-red); diff --git a/assets/js/components/ChargingPlans/PlanRepeatingSettings.vue b/assets/js/components/ChargingPlans/PlanRepeatingSettings.vue index a2122d815..3e5d388ed 100644 --- a/assets/js/components/ChargingPlans/PlanRepeatingSettings.vue +++ b/assets/js/components/ChargingPlans/PlanRepeatingSettings.vue @@ -184,7 +184,7 @@ export default defineComponent({ return this.dataChanged && this.selectedActive; }, weekdaysLabel(): string { - return this.getShortenedWeekdaysLabel(this.selectedWeekdays); + return this.fmtWeekdaysRange(this.selectedWeekdays); }, socOptions(): SelectOption[] { // a list of entries from 5 to 100 with a step of 5 diff --git a/assets/js/components/Config/CurrencyModal.vue b/assets/js/components/Config/CurrencyModal.vue new file mode 100644 index 000000000..847efbaa1 --- /dev/null +++ b/assets/js/components/Config/CurrencyModal.vue @@ -0,0 +1,121 @@ + + + + diff --git a/assets/js/components/Config/DeviceModal/Actions.vue b/assets/js/components/Config/DeviceModal/Actions.vue index 57e877a89..838beef1e 100644 --- a/assets/js/components/Config/DeviceModal/Actions.vue +++ b/assets/js/components/Config/DeviceModal/Actions.vue @@ -4,6 +4,7 @@ v-if="testState" v-bind="testState" :sponsor-token-required="sponsorTokenRequired" + :currency="currency" @test="$emit('test')" /> @@ -51,6 +52,7 @@ import { defineComponent } from "vue"; import type { PropType } from "vue"; import TestResult from "../TestResult.vue"; import { type TestState } from "../utils/test"; +import type { CURRENCY } from "@/types/evcc"; export default defineComponent({ name: "DeviceModalActions", @@ -67,6 +69,7 @@ export default defineComponent({ isSucceeded: Boolean as PropType, isNew: Boolean as PropType, sponsorTokenRequired: Boolean as PropType, + currency: String as PropType, }, emits: ["save", "remove", "test"], computed: { diff --git a/assets/js/components/Config/DeviceModal/DeviceModalBase.vue b/assets/js/components/Config/DeviceModal/DeviceModalBase.vue index 4651ae2dc..40bdd13b7 100644 --- a/assets/js/components/Config/DeviceModal/DeviceModalBase.vue +++ b/assets/js/components/Config/DeviceModal/DeviceModalBase.vue @@ -58,6 +58,7 @@ v-bind="param" v-model="values[param.Name]" :service-values="serviceValues[param.Name]" + :currency="currency" />
@@ -129,6 +130,7 @@ v-bind="param" v-model="values[param.Name]" :service-values="serviceValues[param.Name]" + :currency="currency" /> @@ -140,6 +142,7 @@ v-bind="param" v-model="values[param.Name]" :service-values="serviceValues[param.Name]" + :currency="currency" /> + + diff --git a/assets/js/components/Config/PropertyZoneSummary.vue b/assets/js/components/Config/PropertyZoneSummary.vue new file mode 100644 index 000000000..380ef01cd --- /dev/null +++ b/assets/js/components/Config/PropertyZoneSummary.vue @@ -0,0 +1,69 @@ + + + diff --git a/assets/js/components/Config/PropertyZonesField.vue b/assets/js/components/Config/PropertyZonesField.vue new file mode 100644 index 000000000..0dd4dbd64 --- /dev/null +++ b/assets/js/components/Config/PropertyZonesField.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/assets/js/components/Config/TariffCard.vue b/assets/js/components/Config/TariffCard.vue new file mode 100644 index 000000000..5ecf3046b --- /dev/null +++ b/assets/js/components/Config/TariffCard.vue @@ -0,0 +1,76 @@ + + + diff --git a/assets/js/components/Config/TariffModal.vue b/assets/js/components/Config/TariffModal.vue new file mode 100644 index 000000000..e80326a6f --- /dev/null +++ b/assets/js/components/Config/TariffModal.vue @@ -0,0 +1,255 @@ + + + + diff --git a/assets/js/components/Config/TariffsModal.vue b/assets/js/components/Config/TariffsLegacyModal.vue similarity index 52% rename from assets/js/components/Config/TariffsModal.vue rename to assets/js/components/Config/TariffsLegacyModal.vue index 362cce197..1f91e6817 100644 --- a/assets/js/components/Config/TariffsModal.vue +++ b/assets/js/components/Config/TariffsLegacyModal.vue @@ -1,14 +1,21 @@