Config UI: add tariffs (#26698)

Co-authored-by: Maschga <88616799+Maschga@users.noreply.github.com>
Co-authored-by: andig <cpuidle@gmail.com>
Co-authored-by: andig <andi@evcc.io>
This commit is contained in:
Michael Geers 2026-03-01 12:06:34 +01:00 • committed by GitHub
parent ba159992fd
commit a4ed1080e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 2711 additions and 310 deletions

View file

@ -7,15 +7,20 @@ import (
"maps"
"net/http"
"reflect"
"slices"
"strconv"
"dario.cat/mergo"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/globalconfig"
"github.com/evcc-io/evcc/charger"
"github.com/evcc-io/evcc/core/circuit"
"github.com/evcc-io/evcc/core/keys"
"github.com/evcc-io/evcc/core/site"
"github.com/evcc-io/evcc/messenger"
"github.com/evcc-io/evcc/meter"
"github.com/evcc-io/evcc/server/db/settings"
"github.com/evcc-io/evcc/tariff"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/config"
"github.com/evcc-io/evcc/util/templates"
@ -73,6 +78,9 @@ func devicesConfigHandler(w http.ResponseWriter, r *http.Request) {
case templates.Circuit:
res, err = devicesConfig(class, config.Circuits(), hidePrivate)
case templates.Tariff:
res, err = devicesConfig(class, config.Tariffs(), hidePrivate)
case templates.Messenger:
res, err = devicesConfig(class, config.Messengers(), hidePrivate)
}
@ -196,6 +204,9 @@ func deviceConfigHandler(w http.ResponseWriter, r *http.Request) {
case templates.Circuit:
res, err = deviceConfig(class, id, config.Circuits(), hidePrivate)
case templates.Tariff:
res, err = deviceConfig(class, id, config.Tariffs(), hidePrivate)
case templates.Messenger:
res, err = deviceConfig(class, id, config.Messengers(), hidePrivate)
}
@ -255,6 +266,9 @@ func deviceStatusHandler(w http.ResponseWriter, r *http.Request) {
case templates.Circuit:
instance, err = deviceStatus(name, config.Circuits())
case templates.Tariff:
instance, err = deviceStatus(name, config.Tariffs())
case templates.Messenger:
instance, err = deviceStatus(name, config.Messengers())
}
@ -317,6 +331,9 @@ func newDeviceHandler(w http.ResponseWriter, r *http.Request) {
return circuit.NewFromConfig(ctx, util.NewLogger("circuit"), other)
}, config.Circuits(), force)
case templates.Tariff:
conf, err = newDevice(ctx, class, req, tariff.NewFromConfig, config.Tariffs(), force)
case templates.Messenger:
conf, err = newDevice(ctx, class, req, messenger.NewFromConfig, config.Messengers(), force)
}
@ -401,6 +418,9 @@ func updateDeviceHandler(w http.ResponseWriter, r *http.Request) {
return circuit.NewFromConfig(ctx, util.NewLogger("circuit"), other)
}, config.Circuits(), force)
case templates.Tariff:
err = updateDevice(ctx, id, class, req, tariff.NewFromConfig, config.Tariffs(), force)
case templates.Messenger:
err = updateDevice(ctx, id, class, req, messenger.NewFromConfig, config.Messengers(), force)
}
@ -468,6 +488,27 @@ func cleanupSiteMeterRef(name string, get func() []string, set func([]string)) {
}
}
// cleanupTariffRef removes a tariff reference from settings
func cleanupTariffRef(name string) {
if !settings.Exists(keys.TariffRefs) {
return
}
var refs globalconfig.TariffRefs
if err := settings.Json(keys.TariffRefs, &refs); err != nil {
return
}
for _, ref := range []*string{&refs.Grid, &refs.FeedIn, &refs.Co2, &refs.Planner} {
if *ref == name {
*ref = ""
}
}
refs.Solar = slices.DeleteFunc(refs.Solar, func(ref string) bool { return ref == name })
settings.SetJson(keys.TariffRefs, refs)
}
// deleteDeviceHandler deletes a device from database by class
func deleteDeviceHandler(site site.API) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
@ -550,6 +591,14 @@ func deleteDeviceHandler(site site.API) func(w http.ResponseWriter, r *http.Requ
}
}
case templates.Tariff:
err = deleteDevice(id, config.Tariffs())
// cleanup references
if err == nil {
cleanupTariffRef(config.NameForID(id))
}
case templates.Messenger:
err = deleteDevice(id, config.Messengers())
}
@ -623,6 +672,9 @@ func testConfigHandler(w http.ResponseWriter, r *http.Request) {
case templates.Circuit:
err = api.ErrNotAvailable
case templates.Tariff:
instance, err = testConfig(ctx, id, class, req, tariff.NewFromConfig, config.Tariffs())
case templates.Messenger:
instance, err = testConfig(ctx, id, class, req, messenger.NewFromConfig, config.Messengers())
}