diff --git a/.goreleaser.yml b/.goreleaser.yml
index 3ae3440ee9..ead2f893de 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -32,6 +32,10 @@ builds:
goarch: arm
- goos: windows
goarch: arm64
+ overrides:
+ - goos: darwin
+ ldflags:
+ - "-B gobuildid"
env:
- CGO_ENABLED=0
diff --git a/Makefile b/Makefile
index 28233cd953..832337dd0f 100644
--- a/Makefile
+++ b/Makefile
@@ -77,7 +77,7 @@ build::
CGO_ENABLED=0 go build -v $(BUILD_TAGS) $(BUILD_ARGS)
snapshot::
- goreleaser --snapshot --skip-publish --clean
+ goreleaser --snapshot --skip publish --clean
release::
goreleaser --clean
diff --git a/api/api.go b/api/api.go
index 33860d9350..9a01f7de1c 100644
--- a/api/api.go
+++ b/api/api.go
@@ -68,7 +68,7 @@ type CurrentGetter interface {
GetMaxCurrent() (float64, error)
}
-// BatteryController optionally allows to control home battery (dis)charging behaviour
+// BatteryController optionally allows to control home battery (dis)charging behavior
type BatteryController interface {
SetBatteryMode(BatteryMode) error
}
@@ -120,7 +120,7 @@ type Authorizer interface {
Authorize(key string) error
}
-// PhaseDescriber returns the number of availablephases
+// PhaseDescriber returns the number of available phases
type PhaseDescriber interface {
Phases() int
}
diff --git a/api/rates.go b/api/rates.go
index 578a59aa04..14ccfb1085 100644
--- a/api/rates.go
+++ b/api/rates.go
@@ -1,7 +1,7 @@
package api
import (
- "errors"
+ "fmt"
"slices"
"time"
)
@@ -36,5 +36,9 @@ func (r Rates) Current(now time.Time) (Rate, error) {
}
}
- return Rate{}, errors.New("no matching rate")
+ if len(r) == 0 {
+ return Rate{}, fmt.Errorf("no matching rate for: %s", now.Local().Format(time.RFC3339))
+ }
+ return Rate{}, fmt.Errorf("no matching rate for: %s, %d rates (%s to %s)",
+ now.Local().Format(time.RFC3339), len(r), r[0].Start.Local().Format(time.RFC3339), r[len(r)-1].End.Local().Format(time.RFC3339))
}
diff --git a/assets/js/colors.js b/assets/js/colors.js
index 2ba6fde98c..2e536a66f8 100644
--- a/assets/js/colors.js
+++ b/assets/js/colors.js
@@ -18,7 +18,7 @@ const colors = reactive({
price: "#FF912FFF",
co2: "#00916EFF",
background: null,
- selfPalette: ["#0fde41ff", "#0ba631ff", "#076f20ff", "#054e18ff", "#043611ff", "#02230bff"],
+ selfPalette: ["#0FDE41FF", "#FFBD2FFF", "#FD6158FF", "#03C1EFFF", "#0F662DFF", "#FF922EFF"],
palette: [
"#03C1EFFF",
"#FD6158FF",
diff --git a/assets/js/components/Config/DeviceTags.vue b/assets/js/components/Config/DeviceTags.vue
index 00f779a503..ab9d5e80c8 100644
--- a/assets/js/components/Config/DeviceTags.vue
+++ b/assets/js/components/Config/DeviceTags.vue
@@ -10,11 +10,13 @@
{{ $t(`config.deviceValue.${entry.name}`) }}
{{ fmtDeviceValue(entry) }}
@@ -23,7 +25,9 @@
diff --git a/assets/js/components/Config/PropertyCollapsible.vue b/assets/js/components/Config/PropertyCollapsible.vue
index a01e9a5c0c..218f382f10 100644
--- a/assets/js/components/Config/PropertyCollapsible.vue
+++ b/assets/js/components/Config/PropertyCollapsible.vue
@@ -6,8 +6,8 @@
type="button"
@click="toggle"
>
- Hide advanced settings
- Show advanced settings
+ {{ $t("config.general.hideAdvancedSettings") }}
+ {{ $t("config.general.showAdvancedSettings") }}
diff --git a/assets/js/components/Config/PropertyField.vue b/assets/js/components/Config/PropertyField.vue
index 325aeb6d4c..929bb99bb9 100644
--- a/assets/js/components/Config/PropertyField.vue
+++ b/assets/js/components/Config/PropertyField.vue
@@ -93,10 +93,14 @@
import "@h2d2/shopicons/es/regular/minus";
import VehicleIcon from "../VehicleIcon";
import SelectGroup from "../SelectGroup.vue";
+import formatter from "../../mixins/formatter";
+
+const NS_PER_SECOND = 1000000000;
export default {
name: "PropertyField",
components: { VehicleIcon, SelectGroup },
+ mixins: [formatter],
props: {
id: String,
property: String,
@@ -149,6 +153,9 @@ export default {
if (this.property === "capacity") {
return "kWh";
}
+ if (this.type === "Duration") {
+ return this.fmtSecondUnit(this.value);
+ }
return null;
},
icons() {
@@ -203,6 +210,10 @@ export default {
return Array.isArray(this.modelValue) ? this.modelValue.join("\n") : "";
}
+ if (this.type === "Duration" && typeof this.modelValue === "number") {
+ return this.modelValue / NS_PER_SECOND;
+ }
+
return this.modelValue;
},
set(value) {
@@ -216,6 +227,10 @@ export default {
newValue = value ? value.split("\n") : [];
}
+ if (this.type === "Duration" && typeof newValue === "number") {
+ newValue = newValue * NS_PER_SECOND;
+ }
+
this.$emit("update:modelValue", newValue);
},
},
diff --git a/assets/js/mixins/formatter.js b/assets/js/mixins/formatter.js
index ca15885225..86396f49e4 100644
--- a/assets/js/mixins/formatter.js
+++ b/assets/js/mixins/formatter.js
@@ -246,6 +246,15 @@ export default {
day: "numeric",
}).format(date);
},
+ fmtSecondUnit: function (seconds) {
+ return new Intl.NumberFormat(this.$i18n?.locale, {
+ style: "unit",
+ unit: "second",
+ unitDisplay: "long",
+ })
+ .formatToParts(seconds)
+ .find((part) => part.type === "unit").value;
+ },
fmtMoney: function (amout = 0, currency = "EUR", decimals = true, withSymbol = false) {
const currencyDisplay = withSymbol ? "narrowSymbol" : "code";
const result = new Intl.NumberFormat(this.$i18n?.locale, {
diff --git a/charger/easee.go b/charger/easee.go
index 2d4ffb4c9f..ab5c519ba0 100644
--- a/charger/easee.go
+++ b/charger/easee.go
@@ -52,6 +52,7 @@ type Easee struct {
lastEnergyPollMux sync.Mutex
maxChargerCurrent float64
dynamicChargerCurrent float64
+ dynamicCircuitCurrent [3]float64
current float64
chargerEnabled bool
smartCharging bool
@@ -61,7 +62,6 @@ type Easee struct {
pilotMode string
reasonForNoCurrent int
phaseMode int
- outputPhase int
sessionStartEnergy *float64
currentPower, sessionEnergy, totalEnergy,
currentL1, currentL2, currentL3 float64
@@ -313,8 +313,12 @@ func (c *Easee) ProductUpdate(i json.RawMessage) {
c.currentL3 = value.(float64)
case easee.PHASE_MODE:
c.phaseMode = value.(int)
- case easee.OUTPUT_PHASE:
- c.outputPhase = value.(int) / 10 // API gives 0,10,30 for 0,1,3p
+ case easee.DYNAMIC_CIRCUIT_CURRENT_P1:
+ c.dynamicCircuitCurrent[0] = value.(float64)
+ case easee.DYNAMIC_CIRCUIT_CURRENT_P2:
+ c.dynamicCircuitCurrent[1] = value.(float64)
+ case easee.DYNAMIC_CIRCUIT_CURRENT_P3:
+ c.dynamicCircuitCurrent[2] = value.(float64)
case easee.MAX_CHARGER_CURRENT:
c.maxChargerCurrent = value.(float64)
case easee.DYNAMIC_CHARGER_CURRENT:
@@ -814,7 +818,22 @@ var _ api.PhaseGetter = (*Easee)(nil)
func (c *Easee) GetPhases() (int, error) {
c.mux.RLock()
defer c.mux.RUnlock()
- return c.outputPhase, nil
+ var phases int
+ if c.circuit != 0 {
+ // circuit level controlled charger
+ for _, dcc := range c.dynamicCircuitCurrent {
+ if dcc > 0 {
+ phases++
+ }
+ }
+ } else {
+ // charger level
+ phases = c.phaseMode
+ if phases == 2 { // map automatic to 3p
+ phases = 3
+ }
+ }
+ return phases, nil
}
var _ api.Identifier = (*Easee)(nil)
diff --git a/charger/eebus.go b/charger/eebus.go
index a6c14ade7e..e8fbe9d62b 100644
--- a/charger/eebus.go
+++ b/charger/eebus.go
@@ -331,7 +331,7 @@ func (c *EEBus) writeCurrentLimitData(evEntity spineapi.EntityRemoteInterface, c
}
// if VAS VW is available, limits are completely covered by it
- // this way evcc can fully control the charging behaviour
+ // this way evcc can fully control the charging behavior
if c.writeLoadControlLimitsVASVW(evEntity, limits) {
c.mux.Lock()
defer c.mux.Unlock()
@@ -410,7 +410,7 @@ func (c *EEBus) hasActiveVASVW(evEntity spineapi.EntityRemoteInterface) bool {
return false
}
-// provides support for the special VW VAS ISO15118-2 charging behaviour if supported
+// provides support for the special VW VAS ISO15118-2 charging behavior if supported
// will return false if it isn't supported or successful
//
// this functionality allows to fully control charging without the EV actually having a
diff --git a/charger/homewizard.go b/charger/homewizard.go
index 3a14bf81e0..445503095c 100644
--- a/charger/homewizard.go
+++ b/charger/homewizard.go
@@ -27,6 +27,7 @@ func NewHomeWizardFromConfig(other map[string]interface{}) (api.Charger, error)
cc := struct {
embed `mapstructure:",squash"`
URI string
+ Usage string
StandbyPower float64
Cache time.Duration
}{
@@ -37,12 +38,12 @@ func NewHomeWizardFromConfig(other map[string]interface{}) (api.Charger, error)
return nil, err
}
- return NewHomeWizard(cc.embed, cc.URI, cc.StandbyPower, cc.Cache)
+ return NewHomeWizard(cc.embed, cc.URI, cc.Usage, cc.StandbyPower, cc.Cache)
}
// NewHomeWizard creates HomeWizard charger
-func NewHomeWizard(embed embed, uri string, standbypower float64, cache time.Duration) (*HomeWizard, error) {
- conn, err := homewizard.NewConnection(uri, cache)
+func NewHomeWizard(embed embed, uri string, usage string, standbypower float64, cache time.Duration) (*HomeWizard, error) {
+ conn, err := homewizard.NewConnection(uri, usage, cache)
if err != nil {
return nil, err
}
diff --git a/charger/mennekes-compact.go b/charger/mennekes-compact.go
index d334ab358b..577e22e56c 100644
--- a/charger/mennekes-compact.go
+++ b/charger/mennekes-compact.go
@@ -58,7 +58,7 @@ const (
mennekesRegChargedEnergyTotal = 0x1000 // float32
mennekesAllowed = 1
- mennekesHeartbeatInterval = 8 * time.Second
+ mennekesHeartbeatInterval = 5 * time.Second
mennekesHeartbeatToken = 0x55AA // 21930
)
diff --git a/charger/ocpp/cs_log.go b/charger/ocpp/cs_log.go
index b4c6138317..adb2e81f0c 100644
--- a/charger/ocpp/cs_log.go
+++ b/charger/ocpp/cs_log.go
@@ -2,18 +2,33 @@ package ocpp
import (
"fmt"
+ "strings"
)
func (cs *CS) print(s string) {
- // var ok bool
- // if s, ok = strings.CutPrefix(s, "sent JSON message to"); ok {
- // s = "send" + s
- // } else if s, ok = strings.CutPrefix(s, "received JSON message from"); ok {
- // s = "recv" + s
- // }
- // if ok {
- cs.log.TRACE.Println(s)
+ // for _, p := range []string{
+ // "completed request",
+ // "dispatched request",
+ // "enqueued CALL",
+ // "handling incoming",
+ // "sent CALL",
+ // "started timeout",
+ // "timeout canceled",
+ // } {
+ // if strings.HasPrefix(s, p) {
+ // return
+ // }
// }
+
+ var ok bool
+ if s, ok = strings.CutPrefix(s, "sent JSON message to"); ok {
+ s = "send" + s
+ } else if s, ok = strings.CutPrefix(s, "received JSON message from"); ok {
+ s = "recv" + s
+ }
+ if ok {
+ cs.log.TRACE.Println(s)
+ }
}
func (cs *CS) Debug(args ...interface{}) {
diff --git a/cmd/flags.go b/cmd/flags.go
index 3e977b933a..6f5b363645 100644
--- a/cmd/flags.go
+++ b/cmd/flags.go
@@ -11,6 +11,9 @@ const (
flagIgnoreDatabase = "ignore-db"
flagIgnoreDatabaseDescription = "Run command ignoring service database"
+ flagDisableAuth = "disable-auth"
+ flagDisableAuthDescription = "Disable authentication (dangerous)"
+
flagBatteryMode = "battery-mode"
flagBatteryModeDescription = "Set battery mode (normal, hold, charge)"
flagBatteryModeWait = "battery-mode-wait"
diff --git a/cmd/root.go b/cmd/root.go
index 4fb4ef7ea8..bd331f5c0c 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -18,6 +18,7 @@ import (
"github.com/evcc-io/evcc/server"
"github.com/evcc-io/evcc/server/updater"
"github.com/evcc-io/evcc/util"
+ "github.com/evcc-io/evcc/util/auth"
"github.com/evcc-io/evcc/util/config"
"github.com/evcc-io/evcc/util/pipe"
"github.com/evcc-io/evcc/util/sponsor"
@@ -75,6 +76,8 @@ func init() {
rootCmd.Flags().Bool("profile", false, "Expose pprof profiles")
bind(rootCmd, "profile")
+
+ rootCmd.Flags().Bool(flagDisableAuth, false, flagDisableAuthDescription)
}
// initConfig reads in config file and ENV variables if set
@@ -270,7 +273,13 @@ func runRoot(cmd *cobra.Command, args []string) {
// allow web access for vehicles
configureAuth(conf.Network, config.Instances(config.Vehicles().Devices()), httpd.Router(), valueChan)
- httpd.RegisterSystemHandler(valueChan, cache, func() {
+ auth := auth.New()
+ if ok, _ := cmd.Flags().GetBool(flagDisableAuth); ok {
+ log.WARN.Println("❗❗❗ Authentication is disabled. This is dangerous. Your data and credentials are not protected.")
+ auth.Disable()
+ }
+
+ httpd.RegisterSystemHandler(valueChan, cache, auth, func() {
log.INFO.Println("evcc was stopped by user. OS should restart the service. Or restart manually.")
once.Do(func() { close(stopC) }) // signal loop to end
})
diff --git a/core/loadpoint_plan.go b/core/loadpoint_plan.go
index 389ecbb5b4..a437355978 100644
--- a/core/loadpoint_plan.go
+++ b/core/loadpoint_plan.go
@@ -169,7 +169,7 @@ func (lp *Loadpoint) plannerActive() (active bool) {
// don't stop an already running slot if goal was not met
lp.log.DEBUG.Println("plan: continuing until end of slot")
return true
- case requiredDuration < smallGapDuration:
+ case requiredDuration < smallSlotDuration:
lp.log.DEBUG.Printf("plan: continuing for remaining %v", requiredDuration.Round(time.Second))
return true
case lp.clock.Until(planStart) < smallGapDuration:
diff --git a/core/site.go b/core/site.go
index 4a7d992b16..8e4a4388cc 100644
--- a/core/site.go
+++ b/core/site.go
@@ -338,7 +338,7 @@ func (site *Site) DumpConfig() {
// verify vehicle detection
if vehicles := site.Vehicles().Instances(); len(vehicles) > 1 {
for _, v := range vehicles {
- if _, ok := v.(api.ChargeState); !ok {
+ if _, ok := v.(api.ChargeState); !ok && len(v.Identifiers()) == 0 {
site.log.WARN.Printf("vehicle '%s' does not support automatic detection", v.Title())
}
}
diff --git a/go.mod b/go.mod
index 6ce5b6ce5d..c744b029ad 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/evcc-io/evcc
-go 1.23.0
+go 1.23.3
require (
dario.cat/mergo v1.0.1
@@ -35,7 +35,7 @@ require (
github.com/glebarez/sqlite v1.11.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-playground/validator/v10 v10.22.1
- github.com/go-telegram/bot v1.10.0
+ github.com/go-telegram/bot v1.10.1
github.com/go-viper/mapstructure/v2 v2.2.1
github.com/godbus/dbus/v5 v5.1.0
github.com/gokrazy/updater v0.0.0-20240113102150-4ac511a17e33
diff --git a/go.sum b/go.sum
index 35ad3e1dd1..62c1bf242b 100644
--- a/go.sum
+++ b/go.sum
@@ -209,8 +209,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
-github.com/go-telegram/bot v1.10.0 h1:/Y9ZupcZhYQiAOFE1ETCF8P4836XE42nDkLcWHYTg3g=
-github.com/go-telegram/bot v1.10.0/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM=
+github.com/go-telegram/bot v1.10.1 h1:zwbEjz6ZlqBsyT5EqNqZDYX1ogHIiln1lwYpcK3E8XA=
+github.com/go-telegram/bot v1.10.1/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
diff --git a/i18n/cs.toml b/i18n/cs.toml
index e601f22d80..6aa376013f 100644
--- a/i18n/cs.toml
+++ b/i18n/cs.toml
@@ -38,9 +38,9 @@ current = "Proud"
enabled = "Zapnuto"
energy = "Energie"
odometer = "Ujetých km"
-phaseCurrents = "Proud L1..L3"
-phasePowers = "Výkon L1..L3"
-phaseVoltages = "Napětí L1..L3"
+phaseCurrents = "Proud L1, L2, L3"
+phasePowers = "Výkon L1, L2, L3"
+phaseVoltages = "Napětí L1, L2, L3"
power = "Výkon"
range = "Rozsah"
soc = "SoC"
diff --git a/i18n/da.toml b/i18n/da.toml
index 6fd3e52205..5e275c6c5f 100644
--- a/i18n/da.toml
+++ b/i18n/da.toml
@@ -63,12 +63,13 @@ enabled = "Aktiveret"
energy = "Energi"
feedinPrice = "Indfødningstarif"
gridPrice = "God pris"
+hemsType = "System"
no = "nej"
odometer = "Kilometertæller"
org = "Organisation"
-phaseCurrents = "Strømstyrke L1..L3"
-phasePowers = "Effekt L1..L3"
-phaseVoltages = "Spænding L1..L3"
+phaseCurrents = "Strømstyrke L1, L2, L3"
+phasePowers = "Effekt L1, L2, L3"
+phaseVoltages = "Spænding L1, L2, L3"
power = "Effekt"
powerRange = "Effekt"
range = "Rækkevidde"
@@ -437,7 +438,7 @@ co2 = "⌀ CO₂"
duration = "Varighed"
fallbackName = "Ladepunkt"
power = "Effekt"
-price = "Σ Pris"
+price = "Pris"
remaining = "Resterende tid"
remoteDisabledHard = "{source}: slukket"
remoteDisabledSoft = "{source}: Adaptiv sol-opladning er slukket"
@@ -640,13 +641,33 @@ downloadCsv = "Download som CSV"
energy = "Opladet"
loadpoint = "Ladepunkt"
noData = "Ingen opladninger i denne måned."
-price = "Σ pris"
+price = "Pris"
reallyDelete = "Er du sikker på at du vil slette denne session?"
+showIndividualEntries = "Vis individuelle sessioner"
solar = "solenergi"
title = "Opladnings Sessioner"
total = "Total"
vehicle = "Køretøj"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Pris {byGroup}"
+byGroupLoadpoint = "Ved ladepunktet"
+byGroupVehicle = "Ved køretøjet"
+energy = "Opladet energi"
+energyGrouped = "Sol vs elnet energi"
+energyGroupedByGroup = "Energi {byGroup}"
+energySubSolar = "{value} sol"
+energySubTotal = "{value} total"
+groupedCo2ByGroup = "CO₂-mængde {byGroup}"
+groupedPriceByGroup = "Total pris {byGroup}"
+historyCo2 = "CO₂-Emission"
+historyCo2Sub = "{value} total"
+historyPrice = "Opladningspris"
+historyPriceSub = "{value} total"
+solar = "Sol andel over året"
+solarByGroup = "Solar andel {byGroup}"
+
[sessions.csv]
chargedenergy = "Energi (kWh)"
chargeduration = "Varighed"
@@ -668,6 +689,27 @@ allLoadpoints = "alle ladepunkter"
allVehicles = "alle køretøjer"
filter = "Filter"
+[sessions.group]
+co2 = "Emission"
+grid = "Elnet"
+price = "Pris"
+self = "Sol"
+
+[sessions.groupBy]
+loadpoint = "Opladnings punkt"
+none = "Total"
+vehicle = "Køretøj"
+
+[sessions.period]
+month = "Måned"
+total = "Total"
+year = "År"
+
+[sessions.type]
+co2 = "CO₂"
+price = "Pris"
+solar = "Sol"
+
[settings]
title = "Brugergrænseflade"
diff --git a/i18n/de.toml b/i18n/de.toml
index 6675672eb4..1ddd6c0cc0 100644
--- a/i18n/de.toml
+++ b/i18n/de.toml
@@ -64,9 +64,9 @@ hemsType = "System"
no = "Nein"
odometer = "Kilometerstand"
org = "Organisation"
-phaseCurrents = "Strom L1..L3"
-phasePowers = "Leistung L1..L3"
-phaseVoltages = "Spannung L1..L3"
+phaseCurrents = "Strom L1, L2, L3"
+phasePowers = "Leistung L1, L2, L3"
+phaseVoltages = "Spannung L1, L2, L3"
power = "Leistung"
powerRange = "Leistung"
range = "Reichweite"
@@ -89,12 +89,14 @@ optional = "optional"
cancel = "Abbrechen"
docsLink = "Siehe Dokumentation."
experimental = "Experimentell"
+hideAdvancedSettings = "Erweiterte einstellungen ausblenden"
off = "aus"
on = "an"
password = "Passwort"
readFromFile = "Aus Datei lesen"
remove = "Entfernen"
save = "Speichern"
+showAdvancedSettings = "Erweiterte einstellungen anzeigen"
telemetry = "Telemetrie"
title = "Titel"
diff --git a/i18n/el.toml b/i18n/el.toml
index e096e3a7f2..a2fdce6d66 100644
--- a/i18n/el.toml
+++ b/i18n/el.toml
@@ -61,12 +61,13 @@ enabled = "Ενεργοποιημένο"
energy = "Ενέργεια"
feedinPrice = "Ταρίφα Feed-in"
gridPrice = "Τιμή δικτύου"
+hemsType = "Σύστημα"
no = "όχι"
odometer = "Οδόμετρο"
org = "Οργανισμός"
-phaseCurrents = "Ένταση L1..L3"
-phasePowers = "Ισχύς L1..L3"
-phaseVoltages = "Τάση L1..L3"
+phaseCurrents = "Ένταση L1, L2, L3"
+phasePowers = "Ισχύς L1, L2, L3"
+phaseVoltages = "Τάση L1, L2, L3"
power = "Ισχύς"
powerRange = "Ισχύς"
range = "Αυτονομία"
@@ -422,7 +423,7 @@ co2 = "⌀ CO₂"
duration = "Διάρκεια"
fallbackName = "Σημείο φόρτισης"
power = "Ισχύς"
-price = "Σ Τιμή"
+price = "Κόστος"
remaining = "Απομένει"
remoteDisabledHard = "{source}: απενεργοποιημένο"
remoteDisabledSoft = "{source}: απενεργοποιημένη προσαρμοζόμενη ηλιακή Φ/Β φόρτιση"
@@ -473,7 +474,7 @@ minpv = "Ελαχ+Φ/Β"
now = "Ταχύ"
off = "Κλειστό"
pv = "Φ/Β"
-smart = "Έξυπνο¨"
+smart = "Έξυπνο"
[main.provider]
login = "σύνδεση"
@@ -620,13 +621,33 @@ downloadCsv = "Λήψη ως CSV"
energy = "Φόρτιση"
loadpoint = "Σημείο φόρτισης"
noData = "Δεν υπάρχουν συνεδρίες φόρτισης αυτό το μήνα."
-price = "Σ Τιμή"
+price = "Κόστος"
reallyDelete = "Θέλετε πραγματικά να διαγράψετε αυτή τη συνεδρία;"
+showIndividualEntries = "Εμφάνιση μεμονωμένων συνεδριών"
solar = "Φ/Β"
title = "Συνεδρίες φόρτισης"
total = "Σύνολο"
vehicle = "Όχημα"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Τιμή {byGroup}"
+byGroupLoadpoint = "κατά Σημείο Φόρτισης"
+byGroupVehicle = "κατά Όχημα"
+energy = "Φορτισμένη Ενέργεια"
+energyGrouped = "Ηλιακή Φ/Β vs. Ενέργειας Δικτύου"
+energyGroupedByGroup = "Ενέργεια {byGroup}"
+energySubSolar = "{value} ηλιακή"
+energySubTotal = "{value} σύνολο"
+groupedCo2ByGroup = "Ποσότητα CO₂ {byGroup}"
+groupedPriceByGroup = "Συνολικό Κόστος {byGroup}"
+historyCo2 = "Εκπομπές CO₂"
+historyCo2Sub = "{value} σύνολο"
+historyPrice = "Κόστη Φορτίσεων"
+historyPriceSub = "{value} σύνολο"
+solar = "Ηλιακό Μερίδιο σε Ετήσια βάση"
+solarByGroup = "Ηλιακό Μερίδιο {byGroup}"
+
[sessions.csv]
chargedenergy = "Ενέργεια (kWh)"
chargeduration = "Διάρκεια"
@@ -648,6 +669,27 @@ allLoadpoints = "όλα τα σημεία φόρτισης"
allVehicles = "όλα τα οχήματα"
filter = "Φίλτρο"
+[sessions.group]
+co2 = "Εκπομπές"
+grid = "Δίκτυο"
+price = "Τιμή"
+self = "Φ/Β"
+
+[sessions.groupBy]
+loadpoint = "Σημείο φόρτισης"
+none = "Σύνολο"
+vehicle = "Όχημα"
+
+[sessions.period]
+month = "Μήνας"
+total = "Σύνολο"
+year = "Έτος"
+
+[sessions.type]
+co2 = "CO₂"
+price = "Τιμή"
+solar = "Φ/Β"
+
[settings]
title = "Διεπαφή χρήστη"
@@ -690,7 +732,7 @@ applyToAll = "Εφαρμογή παντού;"
batteryDescription = "Φορτίζει την οικιακή μπαταρία με ενέργεια από το δίκτυο."
cheapTitle = "Φτηνή φόρτιση δικτύου"
cleanTitle = "Φόρτιση δικτύου πράσινης ενέργειας"
-co2Label = "εκπομπές CO2"
+co2Label = "εκπομπές CO₂"
co2Limit = "Όριο CO₂"
loadpointDescription = "Επιτρέπει την προσωρινή γρήγορη φόρτιση σε λειτουργία ηλιακής Φ/Β ενέργειας."
modalTitle = "Έξυπνη φόρτιση δικτύου"
diff --git a/i18n/en.toml b/i18n/en.toml
index d8755d2792..ac2d171869 100644
--- a/i18n/en.toml
+++ b/i18n/en.toml
@@ -64,9 +64,9 @@ hemsType = "System"
no = "no"
odometer = "Odometer"
org = "Organization"
-phaseCurrents = "Current L1..L3"
-phasePowers = "Power L1..L3"
-phaseVoltages = "Voltage L1..L3"
+phaseCurrents = "Current L1, L2, L3"
+phasePowers = "Power L1, L2, L3"
+phaseVoltages = "Voltage L1, L2, L3"
power = "Power"
powerRange = "Power"
range = "Range"
@@ -89,12 +89,14 @@ optional = "optional"
cancel = "Cancel"
docsLink = "See documentation."
experimental = "Experimental"
+hideAdvancedSettings = "Hide advanced settings"
off = "off"
on = "on"
password = "Password"
readFromFile = "Read from file"
remove = "Remove"
save = "Save"
+showAdvancedSettings = "Show advanced settings"
telemetry = "Telemetry"
title = "Title"
diff --git a/i18n/es.toml b/i18n/es.toml
index d19cdd827a..c0d49d10e0 100644
--- a/i18n/es.toml
+++ b/i18n/es.toml
@@ -63,12 +63,13 @@ enabled = "Activado"
energy = "Energía"
feedinPrice = "Precio de entrada"
gridPrice = "Precio de red"
+hemsType = "Sistema"
no = "no"
odometer = "Cuentakilómetros"
org = "Organización"
-phaseCurrents = "Actual L1..L3"
-phasePowers = "Potencia L1..L3"
-phaseVoltages = "Voltaje L1..L3"
+phaseCurrents = "Actual L1, L2, L3"
+phasePowers = "Potencia L1, L2, L3"
+phaseVoltages = "Voltaje L1, L2, L3"
power = "Potencia"
powerRange = "Potencia"
range = "Alcance"
@@ -442,7 +443,7 @@ co2 = "CO₂ ⌀ "
duration = "Duración"
fallbackName = "Punto de carga"
power = "Potencia"
-price = "Σ Precio"
+price = "Precio"
remaining = "Tiempo restante"
remoteDisabledHard = "{source}: desactivado"
remoteDisabledSoft = "{source}: Carga adaptativa desactivada"
@@ -461,6 +462,9 @@ vehicle = "Vehículo"
[main.loadpointSettings.batteryBoost]
description = "Carga rápida desde la batería de casa"
+label = "Aumento de la batería"
+mode = "Solo disponible en modo solar y min+solar."
+once = "Boost activo para esta sesión de carga"
[main.loadpointSettings.limitSoc]
description = "Límite de carga que se utiliza cuando este vehículo está conectado"
@@ -563,6 +567,7 @@ vehicleLimit = "límite de vehículo: {soc}"
[main.vehicleStatus]
awaitingAuthorization = "Esperando autorización"
+batteryBoost = "Refuerzo de batería activo"
charging = "Cargando ..."
cheapEnergyCharging = "Energía barata disponible."
cheapEnergyNextStart = "Energía barata durante {duration}"
@@ -641,13 +646,33 @@ downloadCsv = "Descargar como CSV"
energy = "Cargado"
loadpoint = "Punto de carga"
noData = "No hay sesiones de carga este mes."
-price = "Σ Precio"
+price = "Precio"
reallyDelete = "¿De verdad quieres eliminar esta sesión?"
+showIndividualEntries = "Mostrar sesiones individuales"
solar = "Solar"
title = "Sesiones de carga"
total = "Total"
vehicle = "Vehículo"
+[sessions.chartTitle]
+avgCo2ByGroup = "CO₂ ⌀ {byGroup}"
+avgPriceByGroup = "Precio ⌀ {byGroup}"
+byGroupLoadpoint = "por punto de carga"
+byGroupVehicle = "por vehículo"
+energy = "Energía cargada"
+energyGrouped = "“Energía solar versus energía de red”"
+energyGroupedByGroup = "Energía {byGroup}"
+energySubSolar = "{value} solar"
+energySubTotal = "{value} total"
+groupedCo2ByGroup = "CO₂-Importe {byGroup}"
+groupedPriceByGroup = "Coste total {byGroup}"
+historyCo2 = "Emisiones de CO₂"
+historyCo2Sub = "{value} total"
+historyPrice = "Precio de carga"
+historyPriceSub = "{value} total"
+solar = "Cuota solar anual"
+solarByGroup = "Cuota Solar {byGroup}"
+
[sessions.csv]
chargedenergy = "Energía (kWh)"
chargeduration = "«Duración»"
@@ -669,6 +694,27 @@ allLoadpoints = "todos los puntos de recarga"
allVehicles = "todos los vehiculos"
filter = "Filtrar"
+[sessions.group]
+co2 = "Emisiones"
+grid = "Red eléctrica"
+price = "Precio"
+self = "Energía Solar"
+
+[sessions.groupBy]
+loadpoint = "Punto de carga"
+none = "Total"
+vehicle = "Vehículo"
+
+[sessions.period]
+month = "Mes"
+total = "Total"
+year = "Año"
+
+[sessions.type]
+co2 = "CO₂"
+price = "Precio"
+solar = "Energía Solar"
+
[settings]
title = "Interfaz de usuario"
diff --git a/i18n/et.toml b/i18n/et.toml
new file mode 100644
index 0000000000..0304ead4f6
--- /dev/null
+++ b/i18n/et.toml
@@ -0,0 +1,4 @@
+[batterySettings]
+batteryLevel = "Akutase"
+capacity = "{energy} / {total}"
+control = "Akuhaldus"
diff --git a/i18n/fi.toml b/i18n/fi.toml
index b83c2783ce..96bfe233dc 100644
--- a/i18n/fi.toml
+++ b/i18n/fi.toml
@@ -66,9 +66,9 @@ gridPrice = "Verkosta ostonhinta"
no = "ei"
odometer = "Matkamittari"
org = "Organisaatio"
-phaseCurrents = "Virta L1..L3"
-phasePowers = "Teho L1..L3"
-phaseVoltages = "Jännite L1..L3"
+phaseCurrents = "Virta L1, L2, L3"
+phasePowers = "Teho L1, L2, L3"
+phaseVoltages = "Jännite L1, L2, L3"
power = "Teho"
powerRange = "Teho"
range = "Toimintasäde"
diff --git a/i18n/fr.toml b/i18n/fr.toml
index 0b55a05fc6..fbed2b85b0 100644
--- a/i18n/fr.toml
+++ b/i18n/fr.toml
@@ -63,12 +63,13 @@ enabled = "Activé"
energy = "Énergie"
feedinPrice = "Prix de revente"
gridPrice = "Prix du réseau"
+hemsType = "Système"
no = "non"
odometer = "Kilométrage"
org = "Organisation"
-phaseCurrents = "Courant L1..L3"
-phasePowers = "Puissance L1..L3"
-phaseVoltages = "Voltage L1..L3"
+phaseCurrents = "Courant L1, L2, L3"
+phasePowers = "Puissance L1, L2, L3"
+phaseVoltages = "Voltage L1, L2, L3"
power = "Puissance"
powerRange = "Fourchette puissance"
range = "Autonomie"
@@ -442,7 +443,7 @@ co2 = "CO₂ ⌀"
duration = "Durée"
fallbackName = "Point de chargement"
power = "Puissance"
-price = "Σ Prix"
+price = "Prix"
remaining = "Temps restant"
remoteDisabledHard = "{source} : désactivée"
remoteDisabledSoft = "{source} : charge PV adaptative désactivée"
@@ -656,7 +657,7 @@ historyPriceSubTitle = "{value} totale"
historyPriceTitle = "⌀ {value} Prix de la charge"
loadpoint = "Point de chargement"
noData = "Pas de session de charge ce mois-ci"
-price = "Σ Prix"
+price = "Prix"
reallyDelete = "Voulez-vous vraiment supprimer cette session?"
showIndividualEntries = "Afficher les sessions individuelles"
solar = "Solaire"
@@ -666,6 +667,25 @@ title = "Sessions de charge"
total = "Total"
vehicle = "Véhicule"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Prix {byGroup}"
+byGroupLoadpoint = "par Point de charge"
+byGroupVehicle = "par Véhicule"
+energy = "Énergie chargée"
+energyGrouped = "Énergie Solaire vs. Réseau"
+energyGroupedByGroup = "Énergie {byGroup}"
+energySubSolar = "{value} solaire"
+energySubTotal = "{value} total"
+groupedCo2ByGroup = "Quantité de CO₂ {byGroup}"
+groupedPriceByGroup = "Coût total {byGroup}"
+historyCo2 = "Émissions de CO₂"
+historyCo2Sub = "{value} total"
+historyPrice = "Coûts de recharge"
+historyPriceSub = "{value} total"
+solar = "Part de Solaire par An"
+solarByGroup = "Part de Solaire {byGroup}"
+
[sessions.csv]
chargedenergy = "Énergie (kWh)"
chargeduration = "Durée"
diff --git a/i18n/hr.toml b/i18n/hr.toml
index a6b3b0e005..55b856e05d 100644
--- a/i18n/hr.toml
+++ b/i18n/hr.toml
@@ -63,12 +63,13 @@ enabled = "Omogućeno"
energy = "Energija"
feedinPrice = "Cijena za nabavu"
gridPrice = "Cijena mreže"
+hemsType = "Sustav"
no = "ne"
odometer = "Odometar"
org = "Organizacija"
-phaseCurrents = "Jakost struje L1..L3"
-phasePowers = "Snaga L1..L3"
-phaseVoltages = "Napon L1..L3"
+phaseCurrents = "Jakost struje L1, L2, L3"
+phasePowers = "Snaga L1, L2, L3"
+phaseVoltages = "Napon L1, L2, L3"
power = "Snaga"
powerRange = "Snaga"
range = "Doseg"
@@ -271,7 +272,7 @@ tabTitle = "Živa zajednica"
co2Saved = "{value} ušteđeno"
co2Title = "CO₂ emisija"
configurePriceCo2 = "Nauči kako konfigurirati podatke o cijeni i CO₂."
-footerLong = "{percent} solarne energije"
+footerLong = "{percent} solarna energija"
footerShort = "{percent} solarno"
modalTitle = "Pregled energije punjenja"
moneySaved = "{value} ušteđeno"
@@ -434,7 +435,7 @@ co2 = "⌀ CO₂"
duration = "Trajanje"
fallbackName = "Mjesto punjenja"
power = "Snaga"
-price = "Σ cijena"
+price = "Trošak"
remaining = "Preostalo"
remoteDisabledHard = "{source}: isključeno"
remoteDisabledSoft = "{source}: adaptivno solarno punjenje isključeno"
@@ -648,7 +649,7 @@ historyPriceSubTitle = "ukupno {value}"
historyPriceTitle = "⌀ {value} cijena punjenja"
loadpoint = "Mjesto punjenja"
noData = "U ovom mjesecu nema sesija napajanja."
-price = "Σ cijena"
+price = "Trošak"
reallyDelete = "Stvarno želiš izbrisati ovu sesiju?"
showIndividualEntries = "Prikaži pojedine zapise"
solar = "Solarno"
@@ -658,6 +659,25 @@ title = "Postupci punjenja"
total = "Ukupno"
vehicle = "Vozilo"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ cijena {byGroup}"
+byGroupLoadpoint = "prema mjestu punjenja"
+byGroupVehicle = "po vozilu"
+energy = "Isporučena energija"
+energyGrouped = "Izvor el. energije"
+energyGroupedByGroup = "Energija {byGroup}"
+energySubSolar = "{value} solarna energija"
+energySubTotal = "{value} ukupno"
+groupedCo2ByGroup = "CO₂ količina {byGroup}"
+groupedPriceByGroup = "Ukupna cijena {byGroup}"
+historyCo2 = "CO₂ onečišćenje"
+historyCo2Sub = "{value} ukupno"
+historyPrice = "Cijena punjenja"
+historyPriceSub = "{value} ukupno"
+solar = "Udio solarne energije tokom godine"
+solarByGroup = "Solarna energija {byGroup}"
+
[sessions.csv]
chargedenergy = "Energija (kWh)"
chargeduration = "Trajanje"
diff --git a/i18n/hu.toml b/i18n/hu.toml
index d1b9bd3450..4c601d556d 100644
--- a/i18n/hu.toml
+++ b/i18n/hu.toml
@@ -61,12 +61,13 @@ enabled = "Engedélyezve"
energy = "Energia"
feedinPrice = "Kötelező átvételi ár"
gridPrice = "Villamosenergia ára"
+hemsType = "Rendszer"
no = "nem"
odometer = "Óraállás"
org = "Szervezet"
-phaseCurrents = "Áram L1..L3"
-phasePowers = "Teljesítmény L1..L3"
-phaseVoltages = "Feszültség L1..L3"
+phaseCurrents = "Áram L1, L2, L3"
+phasePowers = "Teljesítmény L1, L2, L3"
+phaseVoltages = "Feszültség L1, L2, L3"
power = "Teljesítmény"
powerRange = "Teljesítmény"
range = "Hatótáv"
@@ -424,13 +425,14 @@ co2 = "⌀ CO₂"
duration = "Időtartam"
fallbackName = "Töltőpont"
power = "Teljesítmény"
-price = "Σ Ár"
+price = "Költség"
remaining = "Hátralévő"
remoteDisabledHard = "{source}: kikapcsolva"
remoteDisabledSoft = "{source}: kikapcsolva, adaptív napelemes töltés"
solar = "Nap"
[main.loadpointSettings]
+batteryUsage = "Otthoni Akkumulátor"
currents = "Töltőáram"
default = "alapértelmezett"
disclaimerHint = "Megjegyzés:"
@@ -440,6 +442,12 @@ smartCostClean = "Tiszta Hálózati Töltés"
title = "Beállítások {0}"
vehicle = "Jármű"
+[main.loadpointSettings.batteryBoost]
+description = "Gyorstöltés otthoni akkumulátorról."
+label = "Akkumulátoros Rásegítés"
+mode = "Csak szolár és min+szolár módban elérhető."
+once = "Rásegítés aktív ehhez a töltési folyamathoz."
+
[main.loadpointSettings.limitSoc]
description = "Töltési korlát, amikor a jármű csatlakoztatva van."
label = "Alapértelmezett limit"
@@ -537,6 +545,7 @@ vehicleLimit = "Jármű limit: {soc}"
[main.vehicleStatus]
awaitingAuthorization = "Hitelesítésre várakozás."
+batteryBoost = "Akkumulátoros rásegítés aktív."
charging = "Töltés…"
cheapEnergyCharging = "Olcsó energia elérhető."
cheapEnergyNextStart = "Olcsó energia {duration}."
@@ -615,13 +624,33 @@ downloadCsv = "Letöltés mint: CSV"
energy = "Töltve"
loadpoint = "Töltőpont"
noData = "Nincs töltési folyamat ebben a hónapban."
-price = "Σ Ár"
+price = "Σ Költség"
reallyDelete = "Biztosan szeretné törölni ezt a folyamatot?"
+showIndividualEntries = "Egyéni munkamenetek megjelenítése"
solar = "Napenergia"
title = "Töltési Folyamatok"
total = "Összesen"
vehicle = "Jármű"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Ár {byGroup}"
+byGroupLoadpoint = "töltőpont szerint"
+byGroupVehicle = "jármű szerint"
+energy = "Töltött Energia"
+energyGrouped = "Napelem vs. Hálózati Energia"
+energyGroupedByGroup = "Energia {byGroup}"
+energySubSolar = "{value} nap"
+energySubTotal = "{value} összesen"
+groupedCo2ByGroup = "CO₂-Mennyiség {byGroup}"
+groupedPriceByGroup = "Összes Költség {byGroup}"
+historyCo2 = "CO₂-Emissziók"
+historyCo2Sub = "{value} összesen"
+historyPrice = "Töltési Költségek"
+historyPriceSub = "{value} összesen"
+solar = "Napelem aránya egy évre vetítve"
+solarByGroup = "Napelem Aránya {byGroup}"
+
[sessions.csv]
chargedenergy = "Energia (kWh)"
chargeduration = "Időtartam"
@@ -643,6 +672,27 @@ allLoadpoints = "minden töltőpont"
allVehicles = "minden jármű"
filter = "Szűrés"
+[sessions.group]
+co2 = "Emissziók"
+grid = "Hálózat"
+price = "Ár"
+self = "Nap"
+
+[sessions.groupBy]
+loadpoint = "Töltőpont"
+none = "Összes"
+vehicle = "Jármű"
+
+[sessions.period]
+month = "Hónap"
+total = "Összes"
+year = "Év"
+
+[sessions.type]
+co2 = "CO₂"
+price = "Ár"
+solar = "Nap"
+
[settings]
title = "Felhasználói felület"
diff --git a/i18n/it.toml b/i18n/it.toml
index 57f09b3af2..a9aab24576 100644
--- a/i18n/it.toml
+++ b/i18n/it.toml
@@ -4,17 +4,17 @@ capacity = "{energy} di {total}"
control = "Controllo batteria"
discharge = "Prevenire la scarica della batteria in modalità veloce oppure quando è attiva una pianificazione."
disclaimerHint = "Nota:"
-disclaimerText = "“Queste impostazioni riguardano solamente la modalità solare. Il comportamento di ricarica è adattato di conseguenza.”"
-gridChargeTab = "Carica un corso dalla rete."
+disclaimerText = "Queste impostazioni riguardano solamente la modalità solare. Il comportamento di ricarica è adattato di conseguenza."
+gridChargeTab = "Carica in corso dalla rete"
legendBottomName = "Priorità alla carica della batteria di casa”"
legendBottomSubline = "Fino al raggiungimento del {soc}."
-legendMiddleName = "“Priorità alla carica del veicolo”"
+legendMiddleName = "Priorità alla carica del veicolo"
legendMiddleSubline = "Quando la batteria supera il {soc}."
legendTitle = "“Come dovrebbe essere utilizzata l’energia solare?”"
legendTopAutostart = "Parte automaticamente"
legendTopName = "Ricarica del veicolo assistita dalla batteria"
legendTopSubline = "quando la batteria supera il {soc}."
-modalTitle = "“Batteria di casa”"
+modalTitle = "Batteria di casa"
usageTab = "Uso della batteria"
[batterySettings.bufferStart]
@@ -61,12 +61,13 @@ enabled = "Abilitato"
energy = "Energia"
feedinPrice = "Prezzo di vendita"
gridPrice = "Prezzo d'acquisto"
+hemsType = "Sistema"
no = "no"
odometer = "Odometro"
org = "Organizzazione"
-phaseCurrents = "Corrente L1..L3"
-phasePowers = "Potenza L1..L3"
-phaseVoltages = "Voltaggio L1..L3"
+phaseCurrents = "Corrente L1, L2, L3"
+phasePowers = "Potenza L1, L2, L3"
+phaseVoltages = "Voltaggio L1, L2, L3"
power = "Potenza"
powerRange = "Potenza"
range = "Intervallo"
@@ -92,6 +93,7 @@ experimental = "Sperimentale"
off = "spento"
on = "acceso"
password = "Password"
+readFromFile = "Importa da file"
remove = "Rimuovi"
save = "Salva"
telemetry = "Telemetria"
@@ -110,7 +112,9 @@ title = "HEMS"
description = "Scrive i dati di ricarica e le altre metriche su InfluxDB. Usa Grafana o altri strumenti per visualizzare i dati."
descriptionToken = "Controlla la documentazione di InfluxDB per imparare a crearne uno. https://docs.influxdata.com/influxdb/v2/admin/"
labelBucket = "Bucket"
+labelCheckInsecure = "Permetti certificati self-signed"
labelDatabase = "Database"
+labelInsecure = "Validazione certificato"
labelOrg = "Società"
labelPassword = "Password"
labelToken = "Token API"
@@ -153,8 +157,11 @@ description = "Connettiti ad un broker MQTT per permettere lo scambio di dati co
descriptionClientId = "Autore del messaggio. Viene usato `evcc-[rand]` se il campo è lasciato vuoto."
descriptionTopic = "Lasciare vuoto per disabilitare la pubblicazione."
labelBroker = "Broker"
+labelCaCert = "Certificato server (CA)"
labelCheckInsecure = "Permetti certificati self-signed"
+labelClientCert = "Certificato client"
labelClientId = "ID Client"
+labelClientKey = "Chiave client"
labelInsecure = "Validazione certificato"
labelPassword = "Password"
labelTopic = "Argomento"
@@ -260,13 +267,13 @@ powerSub2 = "in carica..."
tabTitle = "Live community"
[footer.savings]
-co2Saved = "{value} salvato"
+co2Saved = "{value} di risparmio"
co2Title = "Emissioni di CO₂"
configurePriceCo2 = "Scopri come configurare i dati su prezzi e CO₂."
footerLong = "{percent} di energia solare"
footerShort = "{percent} solare"
modalTitle = "Riepilogo energia caricata"
-moneySaved = "{value} salvato"
+moneySaved = "{value} di risparmio"
percentGrid = "{grid} kWh rete"
percentSelf = "{self} kWh solare"
percentTitle = "Energia solare"
@@ -304,7 +311,7 @@ trial = "Sei in modalità di prova e puoi utilizzare tutte le funzionalità. Ti
victron = "Stai utilizzando evcc sull'hardware Victron Energy e hai accesso a tutte le funzionalità."
[footer.telemetry]
-optIn = "Voglio contribuire ai miei dati."
+optIn = "Voglio contribuire con i miei dati."
optInMoreDetails = "Ulteriori dettagli {0}."
optInMoreDetailsLink = "qui"
optInSponsorship = "È necessaria una sponsorizzazione"
@@ -428,13 +435,14 @@ co2 = "⌀ CO₂"
duration = "Durata"
fallbackName = "Punto di ricarica"
power = "Potenza"
-price = "Σ Prezzo"
+price = "Costo"
remaining = "Rimanente"
remoteDisabledHard = "{source}: Disabilitato"
remoteDisabledSoft = "{source}: Ricarica FV adattiva disabilitata"
solar = "Solare"
[main.loadpointSettings]
+batteryUsage = "Batteria di casa"
currents = "Corrente di carica"
default = "default"
disclaimerHint = "Nota:"
@@ -444,8 +452,14 @@ smartCostClean = "Ricarica Pulita"
title = "Impostazioni {0}"
vehicle = "Veicolo"
+[main.loadpointSettings.batteryBoost]
+description = "Carica rapida dalla batteria di casa."
+label = "Boost da batteria"
+mode = "Disponibile solo in modalità solare e min+solare."
+once = "Boost attivo per questa sessione di ricarica."
+
[main.loadpointSettings.limitSoc]
-description = "Limite di carica che viene applicato quando il veicolo è connesso."
+description = "Limite di carica applicato quando il veicolo è connesso."
label = "Limite di default"
[main.loadpointSettings.maxCurrent]
@@ -542,6 +556,7 @@ vehicleLimit = "Limite veicolo: {soc}"
[main.vehicleStatus]
awaitingAuthorization = "Aspettando l'autorizzazione."
+batteryBoost = "Boost da batteria attivo"
charging = "In carica..."
cheapEnergyCharging = "Corrente economica disponibile."
cheapEnergyNextStart = "Corrente economica in {duration}."
@@ -620,13 +635,33 @@ downloadCsv = "Scarica come CSV"
energy = "Caricato"
loadpoint = "Punto di ricarica"
noData = "Nessuna sessione di ricarica questo mese."
-price = "Σ Prezzo"
+price = "Costo"
reallyDelete = "Vuoi veramente eliminare questa sessione?"
+showIndividualEntries = "Mostra singole sessioni"
solar = "Solare"
title = "Sessioni di ricarica"
total = "Totale"
vehicle = "Veicolo"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Prezzo {byGroup}"
+byGroupLoadpoint = "per Punto di Ricarica"
+byGroupVehicle = "per Veicolo"
+energy = "Energia ricaricata"
+energyGrouped = "Energia Solare vs Rete"
+energyGroupedByGroup = "Energia {byGroup}"
+energySubSolar = "{value} solare"
+energySubTotal = "{value} totale"
+groupedCo2ByGroup = "Quantità CO₂ {byGroup}"
+groupedPriceByGroup = "Costo Totale {byGroup}"
+historyCo2 = "Emissioni CO₂"
+historyCo2Sub = "{value} totale"
+historyPrice = "Costi di ricarica"
+historyPriceSub = "{value} totale"
+solar = "Perc. Solare annua"
+solarByGroup = "Perc. Solare {byGroup}"
+
[sessions.csv]
chargedenergy = "Energia (kWh)"
chargeduration = "Durata"
@@ -648,6 +683,27 @@ allLoadpoints = "tutte le stazioni di ricarica"
allVehicles = "tutte i veicoli"
filter = "Filtra"
+[sessions.group]
+co2 = "Emissioni"
+grid = "Rete"
+price = "Prezzo"
+self = "Solare"
+
+[sessions.groupBy]
+loadpoint = "Punto di ricarica"
+none = "Totale"
+vehicle = "Veicolo"
+
+[sessions.period]
+month = "Mese"
+total = "Totale"
+year = "Anno"
+
+[sessions.type]
+co2 = "CO₂"
+price = "Prezzo"
+solar = "Solare"
+
[settings]
title = "Interfaccia Utente"
diff --git a/i18n/lb.toml b/i18n/lb.toml
index b1b5675dc8..f2eac1736c 100644
--- a/i18n/lb.toml
+++ b/i18n/lb.toml
@@ -60,12 +60,13 @@ enabled = "Ageschalt"
energy = "Energie"
feedinPrice = "Präis fir anzespeisen"
gridPrice = "Netz Präis"
+hemsType = "System"
no = "nee"
odometer = "Kilometerzäler"
org = "Organisatioun"
-phaseCurrents = "Stroum L1..L3"
-phasePowers = "Leeschtung L1..L3"
-phaseVoltages = "Spannung L1..L3"
+phaseCurrents = "Stroum L1, L2, L3"
+phasePowers = "Leeschtung L1, L2, L3"
+phaseVoltages = "Spannung L1, L2, L3"
power = "Leeschtung"
powerRange = "Leeschtung"
range = "Autonomie"
@@ -319,7 +320,7 @@ availableLong = "nei Versioun verfügbar"
modalCancel = "Ofbriechen"
modalDownload = "Download"
modalInstalledVersion = "Aktuell installéiert Versioun"
-modalNoReleaseNotes = "Keng Verëffentlechungsnotizen verfügbar. Méi Informatioun iwwer déi nei Versioun:"
+modalNoReleaseNotes = "Keng Versiounshiweiser verfügbar. Méi Informatioun iwwert déi nei Versioun:"
modalTitle = "Aktualiséierung verfügbar"
modalUpdate = "Installéieren"
modalUpdateNow = "Elo installéieren"
@@ -433,7 +434,7 @@ co2 = "⌀ CO₂"
duration = "Dauer"
fallbackName = "Luedstatioun"
power = "Leeschtung"
-price = "Σ Präis"
+price = "Käschten"
remaining = "Reschtzäit"
remoteDisabledHard = "{source}: Deaktivéiert"
remoteDisabledSoft = "{source}: Adaptatiivt PV-Lueden deaktivéiert"
@@ -647,7 +648,7 @@ historyPriceSubTitle = "{value} insgesamt"
historyPriceTitle = "⌀ {value} Luedpräis"
loadpoint = "Luedstatioun"
noData = "Nach keng Opluedsessiounen fir dëse Mount."
-price = "Σ Präis"
+price = "Käschten"
reallyDelete = "Wëlls du dës Sessioun wierklech läschen?"
showIndividualEntries = "Eenzel Luedsessiounen uweisen"
solar = "Sonn"
@@ -657,6 +658,25 @@ title = "Opluedsessiounen"
total = "Total"
vehicle = "Gefier"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Präis {byGroup}"
+byGroupLoadpoint = "no Opluedpunkt"
+byGroupVehicle = "no Gefier"
+energy = "Geluedene Stroum"
+energyGrouped = "Sonne- vs. Netzstroum"
+energyGroupedByGroup = "Stroum {byGroup}"
+energySubSolar = "{value} Sonn"
+energySubTotal = "{value} insgesamt"
+groupedCo2ByGroup = "Quantitéit CO₂ {byGroup}"
+groupedPriceByGroup = "Käschten {byGroup}"
+historyCo2 = "CO₂-Emissiounen"
+historyCo2Sub = "{value} insgesamt"
+historyPrice = "Opluedkäschten"
+historyPriceSub = "{value} insgesamt"
+solar = "Sonnenundeel iwwer d'Joer"
+solarByGroup = "Sonnenundeel {byGroup}"
+
[sessions.csv]
chargedenergy = "Energie (kWh)"
chargeduration = "Oplueddauer"
@@ -745,7 +765,7 @@ co2Label = "CO₂-Emissioun"
co2Limit = "CO₂-Grenz"
loadpointDescription = "Aktivéiert iwwerganksméisseg Schnelloplueden am PV-Modus."
modalTitle = "Smart Opluede vum Netz"
-none = "keent"
+none = "keng"
priceLabel = "Energiepräis"
priceLimit = "Präisgrenz"
saved = "Gepäichert."
diff --git a/i18n/lt.toml b/i18n/lt.toml
index 15b435865d..17bb4e46b8 100644
--- a/i18n/lt.toml
+++ b/i18n/lt.toml
@@ -63,12 +63,13 @@ enabled = "Aktyvuota"
energy = "Energija"
feedinPrice = "Tiekimo į tinklą kaina"
gridPrice = "Energijos pirkimo kaina"
+hemsType = "Sistema"
no = "ne"
odometer = "Odometras"
org = "Organizacija"
-phaseCurrents = "Srovė L1..L3"
-phasePowers = "Galia L1..L3"
-phaseVoltages = "Įtampa L1..L3"
+phaseCurrents = "Srovė L1, L2, L3"
+phasePowers = "Galia L1, L2, L3"
+phaseVoltages = "Įtampa L1, L2, L3"
power = "Galia"
powerRange = "Galia"
range = "Nuvažiuojamas atstumas"
@@ -298,7 +299,7 @@ tabTitle = "Mano"
30d = "paskutinės 30 dienų"
365d = "paskutinės 365 dienos"
thisYear = "šiais metais"
-total = "Iš viso"
+total = "Nuo pradžios"
[footer.sponsor]
becomeSponsor = "Tapkite rėmėju"
@@ -442,7 +443,7 @@ co2 = "⌀ CO₂"
duration = "Trukmė"
fallbackName = "Įkroviklis"
power = "Galia"
-price = "Σ Kaina"
+price = "Kaina"
remaining = "Liko"
remoteDisabledHard = "{source}: išjungtas"
remoteDisabledSoft = "{source}: adaptyvus Saulės įkrovimas išjungtas"
@@ -659,16 +660,35 @@ historyPriceSubTitle = "{value} viso"
historyPriceTitle = "⌀ {value} Įkrovimo Kaina"
loadpoint = "Įkroviklis"
noData = "Šį mėnesį įkrovimų nebuvo."
-price = "Σ Kaina"
+price = "Kaina"
reallyDelete = "Ar tikrai norite ištrinti šią įkrovimo sesiją?"
showIndividualEntries = "Rodyti individualias sesijas"
solar = "Saulės"
solarTitle = "Saulės dalis per metus"
solarTitleByGroup = "Saulės {byGroup}"
title = "Įkrovimo Sesijos"
-total = "Viso"
+total = "Suma"
vehicle = "Automobilis"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Kaina {byGroup}"
+byGroupLoadpoint = "pagal Įkroviklį"
+byGroupVehicle = "pagal Automobilį"
+energy = "Įkrauta Energija"
+energyGrouped = "Saulės vs. Tinklo Energija"
+energyGroupedByGroup = "Energija {byGroup}"
+energySubSolar = "{value} saulės"
+energySubTotal = "{value} suma"
+groupedCo2ByGroup = "CO₂-Kiekis {byGroup}"
+groupedPriceByGroup = "Bendra Kaina {byGroup}"
+historyCo2 = "CO₂-Tarša"
+historyCo2Sub = "{value} suma"
+historyPrice = "Įkrovimo išlaidos"
+historyPriceSub = "{value} suma"
+solar = "Saulės energijos dalis per metus"
+solarByGroup = "Saulės energijos dalis {byGroup}"
+
[sessions.csv]
chargedenergy = "Energija (kWh)"
chargeduration = "Trukmė"
@@ -698,12 +718,12 @@ self = "Saulės"
[sessions.groupBy]
loadpoint = "Įkroviklis"
-none = "Viso"
+none = "Suma"
vehicle = "Automobilis"
[sessions.period]
month = "Mėnesis"
-total = "Viso"
+total = "Suma"
year = "Metai"
[sessions.type]
diff --git a/i18n/nl.toml b/i18n/nl.toml
index b521bb68cd..dc931a333d 100644
--- a/i18n/nl.toml
+++ b/i18n/nl.toml
@@ -31,10 +31,13 @@ titleAdd = "Voeg accu toe"
titleEdit = "Accu aanpassen"
[config.circuits]
+description = "Zorgt ervoor dat de som van alle laadpunten die zijn aangesloten op een circuit de ingestelde vermogens- en stroomlimieten niet overschrijdt. Circuits kunnen genest worden om een hiërarchie op te bouwen."
title = "Belastingbeheer"
[config.control]
description = "Normaalgesproken zijn de standaardwaardes in orde. Pas deze alleen aan als je weet wat je doet."
+descriptionInterval = "Updatecyclus van de regeling in seconden. Bepaalt hoe vaak evcc metergegevens uitleest, het laadvermogen aanpast en de gebruikersinterface bijwerkt. Korte intervallen (< 30s) kunnen oscillaties en ongewenst gedrag veroorzaken."
+descriptionResidualPower = "Verplaatst het werkpunt van de regelkring. Als je een thuisbatterij hebt, wordt aanbevolen om een waarde van 100 W in te stellen. Op deze manier krijgt de batterij een lichte prioriteit boven het gebruik van het net."
labelInterval = "Update interval"
labelResidualPower = "Vermogensoverschot"
title = "Regelgedrag"
@@ -60,12 +63,13 @@ enabled = "Ingeschakeld"
energy = "Energie"
feedinPrice = "Terugleververgoeding"
gridPrice = "Netprijs"
+hemsType = "Systeem"
no = "nee"
odometer = "Kilometerstand"
org = "Organisatie"
-phaseCurrents = "Stroomsterkte L1..L3"
-phasePowers = "Vermogen L1..L3"
-phaseVoltages = "Spanning L1..L3"
+phaseCurrents = "Stroomsterkte L1, L2, L3"
+phasePowers = "Vermogen L1, L2, L3"
+phaseVoltages = "Spanning L1, L2, L3"
power = "Vermogen"
powerRange = "Vermogen"
range = "Actieradius"
@@ -110,6 +114,7 @@ title = "HEMS"
description = "Schrijft laad-data en andere cijfers naar InfluxDB. Gebruik Grafana of andere tools om de data te visualiseren."
descriptionToken = "Bekijk de InfluxDB documentatie om te zien hoe je er een maakt. https://docs.influxdata.com/influxdb/v2/admin/"
labelBucket = "Bucket"
+labelCheckInsecure = "Sta zelfondertekende certificaten toe"
labelDatabase = "Database"
labelInsecure = "Certificaat validatie"
labelOrg = "Organisatie"
@@ -274,7 +279,7 @@ configurePriceCo2 = "Meer informatie rond het configureren van prijs en CO₂ ge
footerLong = "{percent} zonne-energie"
footerShort = "{percent} zon"
modalTitle = "Oplaadenergie-overzicht"
-moneySaved = "{value} opgeslagen"
+moneySaved = "{value} bespaard"
percentGrid = "{grid} kWh net"
percentSelf = "{self} kWh zon"
percentTitle = "Zonne-energie"
@@ -438,7 +443,7 @@ co2 = "⌀ CO₂"
duration = "Duur"
fallbackName = "Laadpunt"
power = "Vermogen"
-price = "Σ Prijs"
+price = "Kost"
remaining = "Resterend"
remoteDisabledHard = "{source}: uitgeschakeld"
remoteDisabledSoft = "{source}: adaptief PV-laden uitgeschakeld"
@@ -569,7 +574,7 @@ cleanEnergyNextStart = "Schone energie in {duration}."
cleanEnergySet = "CO₂ limiet ingesteld."
climating = "Voor-conditionering gedetecteerd."
connected = "Verbonden."
-disconnectRequired = "Sessie afgebroken. Verbind opnieuw."
+disconnectRequired = "Sessie beëindigd. Gelieve opnieuw verbinding te maken."
disconnected = "Niet verbonden."
finished = "Geëindigd."
minCharge = "Minimaal opladen naar {soc}."
@@ -652,7 +657,7 @@ historyPriceSubTitle = "{value} totaal"
historyPriceTitle = "⌀ {value} Laadprijs"
loadpoint = "Oplaadpunt"
noData = "Geen oplaadsessies deze maand"
-price = "Σ Prijs"
+price = "Kost"
reallyDelete = "Wilt u deze sessie echt verwijderen?"
showIndividualEntries = "Toon individuele sessies"
solar = "Zon"
@@ -662,6 +667,25 @@ title = "Oplaadsessies"
total = "Totaal"
vehicle = "Voertuig"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Prijs {byGroup}"
+byGroupLoadpoint = "per Oplaadpunt"
+byGroupVehicle = "per Voertuig"
+energy = "Geladen Energie"
+energyGrouped = "Zon vs. Net Energie"
+energyGroupedByGroup = "Energie {byGroup}"
+energySubSolar = "{value} zon"
+energySubTotal = "{value} totaal"
+groupedCo2ByGroup = "CO₂-Hoeveelheid {byGroup}"
+groupedPriceByGroup = "Totale Kost {byGroup}"
+historyCo2 = "CO₂-Uitstoot"
+historyCo2Sub = "{value} totaal"
+historyPrice = "Oplaadkosten"
+historyPriceSub = "{value} totaal"
+solar = "Zonne-aandeel per jaar"
+solarByGroup = "Zonne-aandeel {byGroup}"
+
[sessions.csv]
chargedenergy = "Energie (kWh)"
chargeduration = "Duur"
diff --git a/i18n/pl.toml b/i18n/pl.toml
index b250a3c3cd..995428dc98 100644
--- a/i18n/pl.toml
+++ b/i18n/pl.toml
@@ -47,9 +47,9 @@ current = "Prąd"
enabled = "Włączony"
energy = "Energia"
odometer = "Drogomierz"
-phaseCurrents = "Prąd L1..L3"
-phasePowers = "Moc L1..L3"
-phaseVoltages = "Napięcie L1..L3"
+phaseCurrents = "Prąd L1, L2, L3"
+phasePowers = "Moc L1, L2, L3"
+phaseVoltages = "Napięcie L1, L2, L3"
power = "Moc"
range = "Zasięg"
soc = "Stan naładowania"
diff --git a/i18n/pt.toml b/i18n/pt.toml
index f38d3b8af6..c196adb10c 100644
--- a/i18n/pt.toml
+++ b/i18n/pt.toml
@@ -2,7 +2,7 @@
batteryLevel = "Nível da bateria"
capacity = "{energy} de {total}"
control = "Controle de bateria"
-discharge = "Prevenir a descarga no modo rápido e o carregamento planejado."
+discharge = "Prevenir a descarga no modo rápido e a recarga planeada."
disclaimerHint = "Nota:"
disclaimerText = "Estas configurações só afetam o modo solar. O comportamento de carregamento é ajustado de acordo."
gridChargeTab = "Carregamento de rede"
@@ -31,8 +31,8 @@ titleAdd = "Adicionar bateria"
titleEdit = "Editar bateria"
[config.circuits]
-description = "Assegura que a soma de todos os pontos de carga conectados a um circuito não exceda os limites de potência e corrente configurados. Circuitos podem ser aninhados para construir uma hierarquia."
-title = "Gerenciamento de carga"
+description = "Assegura que a soma de todos os pontos de recarga conectados a um circuito não exceda os limites de potência e corrente configurados. Circuitos podem ser combinados para construir uma hierarquia."
+title = "Gerir a recarga"
[config.control]
description = "No geral, os valores padrão estão bem. Apenas mude-os se você sabe o que está fazendo."
@@ -63,12 +63,13 @@ enabled = "Ativado"
energy = "Energia"
feedinPrice = "Preço de alimentação"
gridPrice = "Preço da rede"
+hemsType = "Sistema"
no = "não"
odometer = "Odómetro"
org = "Organização"
-phaseCurrents = "Corrente L1..L3"
-phasePowers = "Energia L1..L3"
-phaseVoltages = "Voltagem L1..L3"
+phaseCurrents = "Corrente L1, L2, L3"
+phasePowers = "Energia L1, L2, L3"
+phaseVoltages = "Voltagem L1, L2, L3"
power = "Energia"
powerRange = "Potência"
range = "Distância"
@@ -126,7 +127,7 @@ v1Support = "Apoio necessário para o InfluxDB 1.x?"
v2Support = "Voltar ao InfluxDB 2.x"
[config.main]
-addLoadpoint = "Adicionar ponto de carga"
+addLoadpoint = "Adicionar ponto de recarga"
addPvBattery = "Adicionar solar ou bateria"
addVehicle = "Adicionar veículo"
configured = "configurado"
@@ -262,7 +263,7 @@ greenEnergySub2 = "desde outubro de 2022"
greenShare = "Parte solar"
greenShareSub1 = "energia fornecida por"
greenShareSub2 = "solar e bateria doméstica"
-power = "Poder de carga"
+power = "Energia de recarga"
powerSub1 = "{activeClients} de {totalClients} utilizadores"
powerSub2 = "carregando..."
tabTitle = "Comunidade ao vivo"
@@ -338,7 +339,7 @@ login = "Registo de veículo"
logout = "sair do sistema"
nativeSettings = "Alterar servidor"
needHelp = "Precisa ajuda?"
-sessions = "Sessões de carga"
+sessions = "Sessões de recarga"
settings = "Configuração"
[header.theme]
@@ -436,9 +437,9 @@ avgPrice = "Preço ⌀"
charged = "Carregado"
co2 = "CO₂ ⌀"
duration = "Duração"
-fallbackName = "Ponto de carga"
+fallbackName = "Ponto de recarga"
power = "Desempenho"
-price = "Preço Σ"
+price = "Custo"
remaining = "Tempo restante"
remoteDisabledHard = "{source}: desativado"
remoteDisabledSoft = "{source}:carregamento PV adaptável desativado"
@@ -446,7 +447,7 @@ solar = "Sol"
[main.loadpointSettings]
batteryUsage = "Bateria de casa"
-currents = "Corrente de carga"
+currents = "Corrente da recarga"
default = "predefinado"
disclaimerHint = "Aviso:"
onlyForSocBasedCharging = "Estas opções só estão disponíveis para veículos com nível de carregamento conhecido."
@@ -456,20 +457,20 @@ title = "Configurações {0}"
vehicle = "Veículo"
[main.loadpointSettings.batteryBoost]
-description = "Carga rápida da bateria de casa."
+description = "Recarga rápida da bateria de casa."
label = "Boost bateria "
mode = "Disponível apenas nos modos solar e min+solar."
-once = "Boost ativo para esta sessão de carga."
+once = "Boost ativo para esta sessão de recarga."
[main.loadpointSettings.limitSoc]
description = "limite de compartilhamento que é usado quando este veículo está conectado."
label = "limite padrão"
[main.loadpointSettings.maxCurrent]
-label = "Corrente de carga máxima"
+label = "Corrente de recarga máxima"
[main.loadpointSettings.minCurrent]
-label = "Corrente de carga mínima"
+label = "Corrente de recarga mínima"
[main.loadpointSettings.minSoc]
description = "O veículo é carregado “rápido” até {0} no modo solar. Em seguida, continua com excedente solar. Útil para garantir um alcance mínimo mesmo em dias mais escuros."
@@ -507,7 +508,7 @@ modalTitle = "Definir hora-alvo"
notReachableInTime = "O objetivo será alcançado {overrun} mais tarde."
onlyInPvMode = "O plano de carregamento só funciona no modo solar."
planDescription = "Entrar um tempo de partida, e evcc irá carregar o veículo o mais rentável ou ambientalmente amigável possível."
-planDuration = "Tempo de carga"
+planDuration = "Tempo de recarga"
planPeriodLabel = "Período"
planPeriodValue = "{start} até {end}"
planUnknown = "desconhecido"
@@ -528,7 +529,7 @@ vehicleCapacityDocs = "Aprender a configurá-la."
vehicleCapacityRequired = "A capacidade da bateria do veículo é necessária para estimar o tempo de carregamento."
[main.targetChargePlan]
-chargeDuration = "Tempo de carga"
+chargeDuration = "Tempo de recarga"
co2Label = "Emissão de CO₂ ⌀"
priceLabel = "Preço da energia"
timeRange = "{day} {range} h"
@@ -574,9 +575,9 @@ connected = "Ligado."
disconnectRequired = "Sessão terminada. Por favor, volte a ligar-se."
disconnected = "Não conectado."
finished = "Concluído."
-minCharge = "Carga mínima até {soc}"
+minCharge = "Recarga mínima até {soc}"
pvDisable = "Excedente insuficiente. Pausa em breve."
-pvEnable = "Excesso disponível. Carga em breve."
+pvEnable = "Excesso disponível. Recarga em breve."
scale1p = "Reduzindo para carregamento monofásico em breve."
scale3p = "Aumentando para carregamento trifásico em breve."
targetChargeActive = "Plano de carregamento ativo. Fim estimado em {duration}."
@@ -586,7 +587,7 @@ unknown = ""
vehicleLimit = "“Limite dos veículos.”"
vehicleLimitReached = "Limite de veículo atingido."
waitForVehicle = "Pronto. Esperando pelo veículo..."
-welcome = "Carga inicial curta para confirmar a ligação."
+welcome = "Recarga inicial curta para confirmar a ligação."
[notifications]
dismissAll = "Recusar tudos"
@@ -626,7 +627,7 @@ meterstop = "Contagem final"
odometer = "Quilometragem"
price = "Preço"
started = "Começado"
-title = "Sessão de carga"
+title = "Sessão de recarga"
[sessions]
avgCo2TitleByGroup = "⌀ CO₂ {byGroup}"
@@ -652,19 +653,38 @@ historyCo2SubTitle = "{value} total"
historyCo2Title = "⌀ {value} Emissões de CO₂"
historyPriceSubTitle = "{value} total"
historyPriceTitle = "⌀ {value} Preço de carregamento"
-loadpoint = "Ponto de carga"
+loadpoint = "Ponto de recarga"
noData = "Nenhuma sessão de carregamento este mês."
odometer = "Quilometragem"
-price = "Preço Σ"
+price = "Custo"
reallyDelete = "Você realmente quer excluir esta sessão?"
showIndividualEntries = "Mostrar sessões individuais"
solar = "Sol"
solarTitle = "“Parte de energia solar ao longo de um ano”"
solarTitleByGroup = "“Parte solar {byGroup}”"
-title = "Sessões de carga"
+title = "Sessões de recarga"
total = "Total"
vehicle = "Veículo"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Preço {byGroup}"
+byGroupLoadpoint = "por ponto de recarga"
+byGroupVehicle = "por veículo"
+energy = "Energia carregada"
+energyGrouped = "Energia Solar vs. Energia de Rede"
+energyGroupedByGroup = "Energia {byGroup}"
+energySubSolar = "{value} solar"
+energySubTotal = "{value} total"
+groupedCo2ByGroup = "Quantidade de CO₂ {byGroup}"
+groupedPriceByGroup = "Custo total {byGroup}"
+historyCo2 = "Emissões de CO₂"
+historyCo2Sub = "{value} total"
+historyPrice = "Custos da recarga"
+historyPriceSub = "{value} total"
+solar = "Parte solar durante todo o ano"
+solarByGroup = "Parte solar {byGroup}"
+
[sessions.csv]
chargedenergy = "Energia (kWh)"
chargeduration = "Duração"
@@ -672,7 +692,7 @@ co2perkwh = "CO₂/kWh"
created = "Hora de início"
finished = "Hora final"
identifier = "Identificador"
-loadpoint = "Ponto de carga"
+loadpoint = "Ponto de recarga"
meterstart = "Início do contador (kWh)"
meterstop = "Contagem final (kWh)"
odometer = "Quilometragem (km)"
@@ -693,7 +713,7 @@ price = "Preço"
self = "Solar"
[sessions.groupBy]
-loadpoint = "“Ponto de recarga”"
+loadpoint = "Ponto de recarga"
none = "Total"
vehicle = "Veiculo"
diff --git a/i18n/ro.toml b/i18n/ro.toml
index 07153b4d3f..5fb3fdd893 100644
--- a/i18n/ro.toml
+++ b/i18n/ro.toml
@@ -36,9 +36,9 @@ current = "Curent"
enabled = "Activat"
energy = "Energie"
odometer = "Kilometraj"
-phaseCurrents = "Curent L1..L3"
-phasePowers = "Putere L1..L3"
-phaseVoltages = "Voltaj L1..L3"
+phaseCurrents = "Curent L1, L2, L3"
+phasePowers = "Putere L1, L2, L3"
+phaseVoltages = "Voltaj L1, L2, L3"
power = "Putere"
range = "Autonomie"
soc = "Stare de încărcare"
diff --git a/i18n/sl.toml b/i18n/sl.toml
index 20a531083e..5dcc548c46 100644
--- a/i18n/sl.toml
+++ b/i18n/sl.toml
@@ -63,12 +63,13 @@ enabled = "Omogočeno"
energy = "Energija"
feedinPrice = "Cena prejete energije iz omrežja"
gridPrice = "Cena omrežja"
+hemsType = "Sistem"
no = "ne"
odometer = "Število kilometrov"
org = "Organizacija"
-phaseCurrents = "Tok L1..L3"
-phasePowers = "Moč L1..L3"
-phaseVoltages = "Napetost L1..L3"
+phaseCurrents = "Tok L1, L2, L3"
+phasePowers = "Moč L1, L2, L3"
+phaseVoltages = "Napetost L1, L2, L3"
power = "Moč"
powerRange = "Moč"
range = "Doseg"
@@ -437,7 +438,7 @@ co2 = "⌀ CO₂"
duration = "Trajanje"
fallbackName = "Polnilno mesto"
power = "Moč"
-price = "Σ Cena"
+price = "Cena"
remaining = "Preostalo"
remoteDisabledHard = "{source}: izključeno"
remoteDisabledSoft = "{source}: izklopljeno prilagodljivo solarno polnjenje"
@@ -638,13 +639,33 @@ downloadCsv = "Prenesi kot CSV"
energy = "Napolnjeno"
loadpoint = "Polnilno mesto"
noData = "Ta mesec ni bilo sej polnjenja."
-price = "Σ Cena"
+price = "Cena"
reallyDelete = "Ali res želite izbrisati to sejo?"
+showIndividualEntries = "Prikaži posamezne seje"
solar = "Sončna energija"
title = "Seje polnjenja"
total = "Skupaj"
vehicle = "Vozilo"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Cena {byGroup}"
+byGroupLoadpoint = "po polnilnem mestu"
+byGroupVehicle = "po vozilu"
+energy = "Napolnjena energija"
+energyGrouped = "Sončna energija v primerjavi z omrežno energijo"
+energyGroupedByGroup = "Energija {byGroup}"
+energySubSolar = "{value} sončna energija"
+energySubTotal = "{value} skupaj"
+groupedCo2ByGroup = "CO₂-Količina {byGroup}"
+groupedPriceByGroup = "Skupni stroški {byGroup}"
+historyCo2 = "CO₂-Emisije"
+historyCo2Sub = "{value} skupaj"
+historyPrice = "Stroški polnjenja"
+historyPriceSub = "{value} skupaj"
+solar = "Solarni delež čez leto"
+solarByGroup = "Solarni delež {byGroup}"
+
[sessions.csv]
chargedenergy = "Energija (kWh)"
chargeduration = "Trajanje"
@@ -666,6 +687,27 @@ allLoadpoints = "vsa polnilna mesta"
allVehicles = "vsa vozila"
filter = "Filter"
+[sessions.group]
+co2 = "Emisije"
+grid = "Omrežje"
+price = "Cena"
+self = "Sončna energija"
+
+[sessions.groupBy]
+loadpoint = "Polnilno mesto"
+none = "Skupaj"
+vehicle = "Vozilo"
+
+[sessions.period]
+month = "Mesec"
+total = "Skupaj"
+year = "Leto"
+
+[sessions.type]
+co2 = "CO₂"
+price = "Cena"
+solar = "Sončna energija"
+
[settings]
title = "Uporabniški vmesnik"
diff --git a/i18n/sv.toml b/i18n/sv.toml
index 611a3c3cec..65a3f8a83e 100644
--- a/i18n/sv.toml
+++ b/i18n/sv.toml
@@ -63,12 +63,13 @@ enabled = "Aktiverad"
energy = "Energi"
feedinPrice = "Inmatningspris"
gridPrice = "Nätpris"
+hemsType = "System"
no = "nej"
odometer = "Mätarställning"
org = "Organisation"
-phaseCurrents = "Ström L1..L3"
-phasePowers = "Effekt L1..L3"
-phaseVoltages = "Spänning L1..L3"
+phaseCurrents = "Ström L1, L2, L3"
+phasePowers = "Effekt L1, L2, L3"
+phaseVoltages = "Spänning L1, L2, L3"
power = "Effekt"
powerRange = "Kraft"
range = "Räckvidd"
@@ -437,13 +438,14 @@ co2 = "⌀ CO₂"
duration = "Varaktighet"
fallbackName = "Laddplats"
power = "Effekt"
-price = "Σ pris"
+price = "Kostnad"
remaining = "Återstående tid"
remoteDisabledHard = "{source}: avstängd"
remoteDisabledSoft = "{source}: Adaptiv solladdning avstängd"
solar = "Solenergi"
[main.loadpointSettings]
+batteryUsage = "Hembatteri"
currents = "Laddström"
default = "förvald"
disclaimerHint = "Anmärkning:"
@@ -453,6 +455,12 @@ smartCostClean = "Laddning med grön el"
title = "Inställningar {0}"
vehicle = "Fordon"
+[main.loadpointSettings.batteryBoost]
+description = "Snabbladdning från hembatteri"
+label = "Batteri Boost"
+mode = "Endast tillgänglig vid 'Sol' och 'Min+Sol' läge."
+once = "Boost är aktiverat för denna laddning."
+
[main.loadpointSettings.limitSoc]
description = "Laddbegränsning när detta fordon är anslutet."
label = "Förvald laddgräns"
@@ -554,6 +562,7 @@ vehicleLimit = "Fordonsgräns: {soc}"
[main.vehicleStatus]
awaitingAuthorization = "Väntar på godkännande."
+batteryBoost = "Batteriboost är aktiv."
charging = "Laddar..."
cheapEnergyCharging = "Billig energi är tillgänglig."
cheapEnergyNextStart = "Billig energi om {duration}."
@@ -632,13 +641,33 @@ downloadCsv = "Ladda hem som CSV"
energy = "Laddat"
loadpoint = "Laddplats"
noData = "Inga laddningar denna månad"
-price = "Σ pris"
+price = "Kostnad"
reallyDelete = "Vill du verkligen radera den här sessionen?"
+showIndividualEntries = "Se enskilda sessioner"
solar = "Sol"
title = "Laddningar"
total = "Total"
vehicle = "Fordon"
+[sessions.chartTitle]
+avgCo2ByGroup = "⌀ CO₂ {byGroup}"
+avgPriceByGroup = "⌀ Pris {byGroup}"
+byGroupLoadpoint = "per laddare"
+byGroupVehicle = "per fordon"
+energy = "Laddad energi"
+energyGrouped = "Sol vs. nätenergi"
+energyGroupedByGroup = "Energi {byGroup}"
+energySubSolar = "{value} sol"
+energySubTotal = "{value} total"
+groupedCo2ByGroup = "CO₂-mängd {byGroup}"
+groupedPriceByGroup = "Total kostnad {byGroup}"
+historyCo2 = "CO₂-Emissioner"
+historyCo2Sub = "{value} total"
+historyPrice = "Laddkostnad"
+historyPriceSub = "{value} total"
+solar = "Solandel över året"
+solarByGroup = "Solandel {byGroup}"
+
[sessions.csv]
chargedenergy = "Energi (kWh)"
chargeduration = "Varaktighet"
@@ -660,6 +689,27 @@ allLoadpoints = "alla laddplatser"
allVehicles = "alla fordon"
filter = "Filter"
+[sessions.group]
+co2 = "Emissioner"
+grid = "Nät"
+price = "Pris"
+self = "Sol"
+
+[sessions.groupBy]
+loadpoint = "Laddare"
+none = "Total"
+vehicle = "Fordon"
+
+[sessions.period]
+month = "Månad"
+total = "Total"
+year = "År"
+
+[sessions.type]
+co2 = "CO₂"
+price = "Pris"
+solar = "Sol"
+
[settings]
title = "Användargränssnitt"
diff --git a/i18n/tr.toml b/i18n/tr.toml
index 357c76edea..d83f8df379 100644
--- a/i18n/tr.toml
+++ b/i18n/tr.toml
@@ -64,9 +64,9 @@ gridPrice = "Alım fiyatı"
no = "hayır"
odometer = "Kilometre Sayacı"
org = "Organizasyon"
-phaseCurrents = "Faz Akımı L1..L3"
-phasePowers = "Faz Gücü L1..L3"
-phaseVoltages = "Faz Voltajı L1..L3"
+phaseCurrents = "Faz Akımı L1, L2, L3"
+phasePowers = "Faz Gücü L1, L2, L3"
+phaseVoltages = "Faz Voltajı L1, L2, L3"
power = "Güç"
powerRange = "“Güç”"
range = "Menzil"
diff --git a/i18n/uk.toml b/i18n/uk.toml
index a9554c848c..eb761cdd8d 100644
--- a/i18n/uk.toml
+++ b/i18n/uk.toml
@@ -59,9 +59,9 @@ enabled = "Ввімкнено"
energy = "Енергія"
no = "ні"
odometer = "Одометр"
-phaseCurrents = "Cтрум L1..L3"
-phasePowers = "Потужність L1..L3"
-phaseVoltages = "Напруга L1..L3"
+phaseCurrents = "Cтрум L1, L2, L3"
+phasePowers = "Потужність L1, L2, L3"
+phaseVoltages = "Напруга L1, L2, L3"
power = "Потужність"
range = "Дальність"
soc = "SoC"
diff --git a/meter/homewizard.go b/meter/homewizard.go
index 52d2f1d5bc..73233b4f11 100644
--- a/meter/homewizard.go
+++ b/meter/homewizard.go
@@ -22,6 +22,7 @@ func init() {
func NewHomeWizardFromConfig(other map[string]interface{}) (api.Meter, error) {
cc := struct {
URI string
+ Usage string
Cache time.Duration
}{
Cache: time.Second,
@@ -31,12 +32,12 @@ func NewHomeWizardFromConfig(other map[string]interface{}) (api.Meter, error) {
return nil, err
}
- return NewHomeWizard(cc.URI, cc.Cache)
+ return NewHomeWizard(cc.URI, cc.Usage, cc.Cache)
}
// NewHomeWizard creates HomeWizard meter
-func NewHomeWizard(uri string, cache time.Duration) (*HomeWizard, error) {
- conn, err := homewizard.NewConnection(uri, cache)
+func NewHomeWizard(uri string, usage string, cache time.Duration) (*HomeWizard, error) {
+ conn, err := homewizard.NewConnection(uri, usage, cache)
if err != nil {
return nil, err
}
diff --git a/meter/homewizard/connection.go b/meter/homewizard/connection.go
index 4411eca616..6aa3d3150e 100644
--- a/meter/homewizard/connection.go
+++ b/meter/homewizard/connection.go
@@ -17,13 +17,14 @@ import (
type Connection struct {
*request.Helper
uri string
+ usage string
ProductType string
dataG provider.Cacheable[DataResponse]
stateG provider.Cacheable[StateResponse]
}
// NewConnection creates a homewizard connection
-func NewConnection(uri string, cache time.Duration) (*Connection, error) {
+func NewConnection(uri string, usage string, cache time.Duration) (*Connection, error) {
if uri == "" {
return nil, errors.New("missing uri")
}
@@ -32,6 +33,7 @@ func NewConnection(uri string, cache time.Duration) (*Connection, error) {
c := &Connection{
Helper: request.NewHelper(log),
uri: fmt.Sprintf("%s/api", util.DefaultScheme(strings.TrimRight(uri, "/"), "http")),
+ usage: usage,
}
c.Client.Transport = request.NewTripper(log, transport.Insecure())
@@ -100,18 +102,27 @@ func (c *Connection) Enabled() (bool, error) {
// CurrentPower implements the api.Meter interface
func (c *Connection) CurrentPower() (float64, error) {
res, err := c.dataG.Get()
+ if c.usage == "pv" {
+ return -res.ActivePowerW, err
+ }
return res.ActivePowerW, err
}
// TotalEnergy implements the api.MeterEnergy interface
func (c *Connection) TotalEnergy() (float64, error) {
res, err := c.dataG.Get()
+ if c.usage == "pv" {
+ return res.TotalPowerExportT1kWh + res.TotalPowerExportT2kWh + res.TotalPowerExportT3kWh + res.TotalPowerExportT4kWh, err
+ }
return res.TotalPowerImportT1kWh + res.TotalPowerImportT2kWh + res.TotalPowerImportT3kWh + res.TotalPowerImportT4kWh, err
}
// Currents implements the api.PhaseCurrents interface
func (c *Connection) Currents() (float64, float64, float64, error) {
res, err := c.dataG.Get()
+ if c.usage == "pv" {
+ return -res.ActiveCurrentL1A, -res.ActiveCurrentL2A, -res.ActiveCurrentL3A, err
+ }
return res.ActiveCurrentL1A, res.ActiveCurrentL2A, res.ActiveCurrentL3A, err
}
diff --git a/meter/homewizard/types.go b/meter/homewizard/types.go
index 2ab9d97dc0..68a3110cfe 100644
--- a/meter/homewizard/types.go
+++ b/meter/homewizard/types.go
@@ -21,6 +21,10 @@ type DataResponse struct {
TotalPowerImportT2kWh float64 `json:"total_power_import_t2_kwh"`
TotalPowerImportT3kWh float64 `json:"total_power_import_t3_kwh"`
TotalPowerImportT4kWh float64 `json:"total_power_import_t4_kwh"`
+ TotalPowerExportT1kWh float64 `json:"total_power_export_t1_kwh"`
+ TotalPowerExportT2kWh float64 `json:"total_power_export_t2_kwh"`
+ TotalPowerExportT3kWh float64 `json:"total_power_export_t3_kwh"`
+ TotalPowerExportT4kWh float64 `json:"total_power_export_t4_kwh"`
ActiveCurrentL1A float64 `json:"active_current_l1_a"`
ActiveCurrentL2A float64 `json:"active_current_l2_a"`
ActiveCurrentL3A float64 `json:"active_current_l3_a"`
diff --git a/meter/homewizard/types_test.go b/meter/homewizard/types_test.go
index b05e889351..381c5844fd 100644
--- a/meter/homewizard/types_test.go
+++ b/meter/homewizard/types_test.go
@@ -33,15 +33,16 @@ func TestUnmarshalStateResponse(t *testing.T) {
}
}
-// Test DataResponse
-func TestUnmarshalDataResponse(t *testing.T) {
+// Test homewizard kWh Meter 1-Phase response
+func TestUnmarshalKwhDataResponse(t *testing.T) {
{
var res DataResponse
-
+ // https://www.homewizard.com/shop/wi-fi-kwh-meter-1-phase/
jsonstr := `{"wifi_ssid": "My Wi-Fi","wifi_strength": 100,"total_power_import_t1_kwh": 30.511,"total_power_export_t1_kwh": 85.951,"active_power_w": 543,"active_power_l1_w": 28,"active_power_l2_w": 0,"active_power_l3_w": -181,"active_voltage_l1_v": 235.4,"active_voltage_l2_v": 235.8,"active_voltage_l3_v": 236.1,"active_current_l1_a": 1.19,"active_current_l2_a": 0.37,"active_current_l3_a": -0.93}`
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
assert.Equal(t, float64(30.511), res.TotalPowerImportT1kWh+res.TotalPowerImportT2kWh+res.TotalPowerImportT3kWh+res.TotalPowerImportT4kWh)
+ assert.Equal(t, float64(85.951), res.TotalPowerExportT1kWh+res.TotalPowerExportT2kWh+res.TotalPowerExportT3kWh+res.TotalPowerExportT4kWh)
assert.Equal(t, float64(543), res.ActivePowerW)
assert.Equal(t, float64(235.4), res.ActiveVoltageL1V)
assert.Equal(t, float64(235.8), res.ActiveVoltageL2V)
@@ -51,3 +52,23 @@ func TestUnmarshalDataResponse(t *testing.T) {
assert.Equal(t, float64(-0.93), res.ActiveCurrentL3A)
}
}
+
+// Test homewizard P1 Meter response
+func TestUnmarshalP1DataResponse(t *testing.T) {
+ {
+ var res DataResponse
+ // https://www.homewizard.com/shop/wi-fi-p1-meter-rj12-2/
+ jsonstr := `{"wifi_ssid":"redacted","wifi_strength":78,"smr_version":50,"meter_model":"Landis + Gyr","unique_id":"redacted","active_tariff":2,"total_power_import_kwh":18664.997,"total_power_import_t1_kwh":10909.724,"total_power_import_t2_kwh":7755.273,"total_power_export_kwh":13823.608,"total_power_export_t1_kwh":4243.981,"total_power_export_t2_kwh":9579.627,"active_power_w":203.000,"active_power_l1_w":-21.000,"active_power_l2_w":57.000,"active_power_l3_w":168.000,"active_voltage_l1_v":228.000,"active_voltage_l2_v":226.000,"active_voltage_l3_v":225.000,"active_current_a":1.091,"active_current_l1_a":-0.092,"active_current_l2_a":0.252,"active_current_l3_a":0.747,"voltage_sag_l1_count":12.000,"voltage_sag_l2_count":12.000,"voltage_sag_l3_count":19.000,"voltage_swell_l1_count":5055.000,"voltage_swell_l2_count":1950.000,"voltage_swell_l3_count":0.000,"any_power_fail_count":12.000,"long_power_fail_count":2.000,"total_gas_m3":5175.363,"gas_timestamp":241106093006,"gas_unique_id":"redacted","external":[{"unique_id":"redacted","type":"gas_meter","timestamp":241106093006,"value":5175.363,"unit":"m3"}]}`
+ require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
+
+ assert.Equal(t, float64(18664.997), res.TotalPowerImportT1kWh+res.TotalPowerImportT2kWh+res.TotalPowerImportT3kWh+res.TotalPowerImportT4kWh)
+ assert.Equal(t, float64(13823.608), res.TotalPowerExportT1kWh+res.TotalPowerExportT2kWh+res.TotalPowerExportT3kWh+res.TotalPowerExportT4kWh)
+ assert.Equal(t, float64(203), res.ActivePowerW)
+ assert.Equal(t, float64(228), res.ActiveVoltageL1V)
+ assert.Equal(t, float64(226), res.ActiveVoltageL2V)
+ assert.Equal(t, float64(225), res.ActiveVoltageL3V)
+ assert.Equal(t, float64(-0.092), res.ActiveCurrentL1A)
+ assert.Equal(t, float64(0.252), res.ActiveCurrentL2A)
+ assert.Equal(t, float64(0.747), res.ActiveCurrentL3A)
+ }
+}
diff --git a/meter/shelly/connection.go b/meter/shelly/connection.go
index 9fafe41539..388b2d7add 100644
--- a/meter/shelly/connection.go
+++ b/meter/shelly/connection.go
@@ -19,6 +19,8 @@ type Connection struct {
channel int
gen int // Shelly api generation
devicetype string // Shelly device type
+ app string // Shelly device app code
+ profile string // Shelly device profile
}
// NewConnection creates a new Shelly device connection.
@@ -45,6 +47,8 @@ func NewConnection(uri, user, password string, channel int) (*Connection, error)
channel: channel,
gen: resp.Gen,
devicetype: resp.Type,
+ app: resp.App,
+ profile: resp.Profile,
}
conn.Client.Transport = request.NewTripper(log, transport.Insecure())
diff --git a/meter/shelly/switch.go b/meter/shelly/switch.go
index 48dad7fc84..26795a9c61 100644
--- a/meter/shelly/switch.go
+++ b/meter/shelly/switch.go
@@ -42,18 +42,25 @@ func (sh *Switch) CurrentPower() (float64, error) {
}
default:
+ var resem Gen2EmStatusResponse
var res Gen2StatusResponse
- if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil {
- return 0, err
+ if d.app == "Pro3EM" && d.profile == "monophase" {
+ if err := d.execGen2Cmd("Shelly.GetStatus", false, &resem); err != nil {
+ return 0, err
+ }
+ } else {
+ if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil {
+ return 0, err
+ }
}
switch d.channel {
case 1:
- power = res.Switch1.Apower + res.Pm1.Apower
+ power = res.Switch1.Apower + res.Pm1.Apower + resem.Em1.ActPower
case 2:
- power = res.Switch2.Apower + res.Pm2.Apower
+ power = res.Switch2.Apower + res.Pm2.Apower + resem.Em2.ActPower
default:
- power = res.Switch0.Apower + res.Pm0.Apower
+ power = res.Switch0.Apower + res.Pm0.Apower + resem.Em0.ActPower
}
}
@@ -123,18 +130,25 @@ func (sh *Switch) TotalEnergy() (float64, error) {
energy = gen1Energy(d.devicetype, energy)
default:
+ var resem Gen2EmStatusResponse
var res Gen2StatusResponse
- if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil {
- return 0, err
+ if d.app == "Pro3EM" && d.profile == "monophase" {
+ if err := d.execGen2Cmd("Shelly.GetStatus", false, &resem); err != nil {
+ return 0, err
+ }
+ } else {
+ if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil {
+ return 0, err
+ }
}
switch d.channel {
case 1:
- energy = res.Switch1.Aenergy.Total + res.Pm1.Aenergy.Total
+ energy = res.Switch1.Aenergy.Total + res.Pm1.Aenergy.Total + resem.Em1Data.TotalActEnergy - resem.Em1Data.TotalActRetEnergy
case 2:
- energy = res.Switch2.Aenergy.Total + res.Pm2.Aenergy.Total
+ energy = res.Switch2.Aenergy.Total + res.Pm2.Aenergy.Total + resem.Em2Data.TotalActEnergy - resem.Em2Data.TotalActRetEnergy
default:
- energy = res.Switch0.Aenergy.Total + res.Pm0.Aenergy.Total
+ energy = res.Switch0.Aenergy.Total + res.Pm0.Aenergy.Total + resem.Em0Data.TotalActEnergy - resem.Em0Data.TotalActRetEnergy
}
}
diff --git a/meter/shelly/types.go b/meter/shelly/types.go
index c06228c532..5824d9811a 100644
--- a/meter/shelly/types.go
+++ b/meter/shelly/types.go
@@ -8,9 +8,11 @@ type DeviceInfo struct {
Model string `json:"model"`
Type string `json:"type"`
Mac string `json:"mac"`
+ App string `json:"app"`
Auth bool `json:"auth"`
AuthEn bool `json:"auth_en"`
NumMeters int `json:"num_meters"`
+ Profile string `json:"profile"`
}
type Gen2RpcPost struct {
@@ -40,17 +42,34 @@ type Gen2StatusResponse struct {
Pm2 Gen2Switch `json:"pm3:2"`
}
+type Gen2Em struct {
+ Current float64 `json:"current"`
+ Voltage float64 `json:"voltage"`
+ ActPower float64 `json:"act_power"`
+}
+
+type Gen2EmData struct {
+ TotalActEnergy float64 `json:"total_act_energy"`
+ TotalActRetEnergy float64 `json:"total_act_ret_energy"`
+}
+
type Gen2EmStatusResponse struct {
- TotalPower float64 `json:"total_act_power"`
- CurrentA float64 `json:"a_current"`
- CurrentB float64 `json:"b_current"`
- CurrentC float64 `json:"c_current"`
- VoltageA float64 `json:"a_voltage"`
- VoltageB float64 `json:"b_voltage"`
- VoltageC float64 `json:"c_voltage"`
- PowerA float64 `json:"a_act_power"`
- PowerB float64 `json:"b_act_power"`
- PowerC float64 `json:"c_act_power"`
+ TotalPower float64 `json:"total_act_power"`
+ CurrentA float64 `json:"a_current"`
+ CurrentB float64 `json:"b_current"`
+ CurrentC float64 `json:"c_current"`
+ VoltageA float64 `json:"a_voltage"`
+ VoltageB float64 `json:"b_voltage"`
+ VoltageC float64 `json:"c_voltage"`
+ PowerA float64 `json:"a_act_power"`
+ PowerB float64 `json:"b_act_power"`
+ PowerC float64 `json:"c_act_power"`
+ Em0 Gen2Em `json:"em1:0"`
+ Em1 Gen2Em `json:"em1:1"`
+ Em2 Gen2Em `json:"em1:2"`
+ Em0Data Gen2EmData `json:"em1data:0"`
+ Em1Data Gen2EmData `json:"em1data:1"`
+ Em2Data Gen2EmData `json:"em1data:2"`
}
type Gen2EmDataStatusResponse struct {
diff --git a/meter/shelly/types_test.go b/meter/shelly/types_test.go
index ad4006749c..2e4e968e82 100644
--- a/meter/shelly/types_test.go
+++ b/meter/shelly/types_test.go
@@ -67,4 +67,44 @@ func TestUnmarshalGen2StatusResponse(t *testing.T) {
assert.Equal(t, 3551.682, res.Pm0.Aenergy.Total)
assert.Equal(t, 1780.1, res.Pm0.Apower)
}
+
+ {
+ // Shelly Pro 3EM
+ var res Gen2EmStatusResponse
+
+ jsonstr := `{"ble":{},"bthome":{"errors":["bluetooth_disabled"]},"cloud":{"connected":true},"em1:0":{"id":0,"current":3.705,"voltage":242.8,"act_power":598.9,"aprt_power":900.6,"pf":0.66,"freq":50.0,"calibration":"factory"},"em1:1":{"id":1,"current":0.194,"voltage":242.8,"act_power":0.0,"aprt_power":47.2,"pf":0.00,"freq":50.0,"calibration":"factory"},"em1:2":{"id":2,"current":0.027,"voltage":242.8,"act_power":0.0,"aprt_power":6.6,"pf":0.00,"freq":50.0,"calibration":"factory"},"em1data:0":{"id":0,"total_act_energy":3458.24,"total_act_ret_energy":1605.24},"em1data:1":{"id":1,"total_act_energy":2768.67,"total_act_ret_energy":25.49},"em1data:2":{"id":2,"total_act_energy":3.09,"total_act_ret_energy":0.71},"eth":{"ip":null},"modbus":{},"mqtt":{"connected":false},"sys":{"mac":"FCE8C0DBA850","restart_required":false,"time":"19:46","unixtime":1731404780,"uptime":563,"ram_size":247148,"ram_free":110596,"fs_size":524288,"fs_free":176128,"cfg_rev":21,"kvs_rev":0,"schedule_rev":3,"webhook_rev":1,"available_updates":{},"reset_reason":3},"temperature:0":{"id": 0,"tC":39.0, "tF":102.2},"wifi":{"sta_ip":"192.168.40.174","status":"got ip","ssid":"IoT","rssi":-67},"ws":{"connected":false}}`
+ require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
+ // Channel 0 (1)
+ assert.Equal(t, 598.9, res.Em0.ActPower)
+ assert.Equal(t, 3.705, res.Em0.Current)
+ assert.Equal(t, 242.8, res.Em0.Voltage)
+ assert.Equal(t, 3458.24, res.Em0Data.TotalActEnergy)
+ assert.Equal(t, 1605.24, res.Em0Data.TotalActRetEnergy)
+ // Channel 1 (2)
+ assert.Equal(t, 0.0, res.Em1.ActPower)
+ assert.Equal(t, 0.194, res.Em1.Current)
+ assert.Equal(t, 242.8, res.Em1.Voltage)
+ assert.Equal(t, 2768.67, res.Em1Data.TotalActEnergy)
+ assert.Equal(t, 25.49, res.Em1Data.TotalActRetEnergy)
+ // Channel 2 (3)
+ assert.Equal(t, 0.0, res.Em2.ActPower)
+ assert.Equal(t, 0.027, res.Em2.Current)
+ assert.Equal(t, 242.8, res.Em2.Voltage)
+ assert.Equal(t, 3.09, res.Em2Data.TotalActEnergy)
+ assert.Equal(t, 0.71, res.Em2Data.TotalActRetEnergy)
+ }
+}
+
+// Test Shelly device info
+func TestUnmarshalDeviceInfoResponse(t *testing.T) {
+ {
+ // Shelly Pro 3EM
+ var res DeviceInfo
+
+ jsonstr := `{"name":null,"id":"shellypro3em-fce8c0dba900","mac":"FCE8C0DBA900","slot":1,"model":"SPEM-003CEBEU","gen":2,"fw_id":"20241011-114455/1.4.4-g6d2a586","ver":"1.4.4","app":"Pro3EM","auth_en":false,"auth_domain":null,"profile":"monophase"}`
+ require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
+
+ assert.Equal(t, "Pro3EM", res.App)
+ assert.Equal(t, "monophase", res.Profile)
+ }
}
diff --git a/meter/zendure.go b/meter/zendure.go
new file mode 100644
index 0000000000..222dbdc7c5
--- /dev/null
+++ b/meter/zendure.go
@@ -0,0 +1,73 @@
+package meter
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/evcc-io/evcc/api"
+ "github.com/evcc-io/evcc/meter/zendure"
+ "github.com/evcc-io/evcc/util"
+)
+
+func init() {
+ registry.Add("zendure", NewZendureFromConfig)
+}
+
+type Zendure struct {
+ usage string
+ conn *zendure.Connection
+}
+
+// NewZendureFromConfig creates a Zendure meter from generic config
+func NewZendureFromConfig(other map[string]interface{}) (api.Meter, error) {
+ cc := struct {
+ Usage, Account, Serial, Region string
+ Timeout time.Duration
+ }{
+ Region: "EU",
+ Timeout: 30 * time.Second,
+ }
+
+ if err := util.DecodeOther(other, &cc); err != nil {
+ return nil, err
+ }
+
+ conn, err := zendure.NewConnection(strings.ToUpper(cc.Region), cc.Account, cc.Serial, cc.Timeout)
+ if err != nil {
+ return nil, err
+ }
+
+ c := &Zendure{
+ usage: cc.Usage,
+ conn: conn,
+ }
+
+ return c, err
+}
+
+// CurrentPower implements the api.Meter interface
+func (c *Zendure) CurrentPower() (float64, error) {
+ res, err := c.conn.Data()
+ if err != nil {
+ return 0, err
+ }
+
+ switch c.usage {
+ case "pv":
+ return float64(res.SolarInputPower), nil
+ case "battery":
+ return float64(res.GridInputPower) - float64(res.OutputHomePower), nil
+ default:
+ return 0, fmt.Errorf("invalid usage: %s", c.usage)
+ }
+}
+
+// Soc implements the api.Battery interface
+func (c *Zendure) Soc() (float64, error) {
+ res, err := c.conn.Data()
+ if err != nil {
+ return 0, err
+ }
+ return float64(res.ElectricLevel), nil
+}
diff --git a/meter/zendure/connection.go b/meter/zendure/connection.go
new file mode 100644
index 0000000000..8dce525d9d
--- /dev/null
+++ b/meter/zendure/connection.go
@@ -0,0 +1,86 @@
+package zendure
+
+import (
+ "encoding/json"
+ "net"
+ "strconv"
+ "sync"
+ "time"
+
+ "dario.cat/mergo"
+ "github.com/evcc-io/evcc/provider/mqtt"
+ "github.com/evcc-io/evcc/util"
+)
+
+var (
+ mu sync.Mutex
+ connections = make(map[string]*Connection)
+)
+
+type Connection struct {
+ log *util.Logger
+ data *util.Monitor[Data]
+}
+
+func NewConnection(region, account, serial string, timeout time.Duration) (*Connection, error) {
+ mu.Lock()
+ defer mu.Unlock()
+
+ key := account + serial
+ if conn, ok := connections[key]; ok {
+ return conn, nil
+ }
+
+ log := util.NewLogger("zendure")
+ res, err := MqttCredentials(log, region, account, serial)
+ if err != nil {
+ return nil, err
+ }
+
+ client, err := mqtt.NewClient(
+ log,
+ net.JoinHostPort(res.Data.MqttUrl, strconv.Itoa(res.Data.Port)), res.Data.AppKey, res.Data.Secret,
+ "", 0, false, "", "", "",
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ conn := &Connection{
+ log: log,
+ data: util.NewMonitor[Data](timeout),
+ }
+
+ topic := res.Data.AppKey + "/#"
+ if err := client.Listen(topic, conn.handler); err != nil {
+ return nil, err
+ }
+
+ connections[key] = conn
+
+ return conn, nil
+}
+
+func (c *Connection) handler(data string) {
+ var res Payload
+ if err := json.Unmarshal([]byte(data), &res); err != nil {
+ c.log.ERROR.Println(err)
+ return
+ }
+
+ if res.Data == nil {
+ return
+ }
+
+ c.data.SetFunc(func(v Data) Data {
+ if err := mergo.Merge(&v, res.Data); err != nil {
+ c.log.ERROR.Println(err)
+ }
+
+ return v
+ })
+}
+
+func (c *Connection) Data() (Data, error) {
+ return c.data.Get()
+}
diff --git a/meter/zendure/credentials.go b/meter/zendure/credentials.go
new file mode 100644
index 0000000000..a02f6a882a
--- /dev/null
+++ b/meter/zendure/credentials.go
@@ -0,0 +1,39 @@
+package zendure
+
+import (
+ "errors"
+ "net/http"
+
+ "github.com/evcc-io/evcc/util"
+ "github.com/evcc-io/evcc/util/request"
+)
+
+const (
+ EUCredentialsUri = "https://app.zendure.tech/eu/developer/api/apply"
+ GlobalCredentialsUri = "https://app.zendure.tech/v2/developer/api/apply"
+)
+
+func MqttCredentials(log *util.Logger, region, account, serial string) (CredentialsResponse, error) {
+ client := request.NewHelper(log)
+
+ data := CredentialsRequest{
+ SnNumber: serial,
+ Account: account,
+ }
+
+ uri := GlobalCredentialsUri
+ if region == "EU" {
+ uri = EUCredentialsUri
+ }
+
+ req, _ := request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
+
+ var res CredentialsResponse
+ err := client.DoJSON(req, &res)
+
+ if err == nil && !res.Success {
+ err = errors.New(res.Msg)
+ }
+
+ return res, err
+}
diff --git a/meter/zendure/types.go b/meter/zendure/types.go
new file mode 100644
index 0000000000..bdba8ae5a4
--- /dev/null
+++ b/meter/zendure/types.go
@@ -0,0 +1,60 @@
+package zendure
+
+type CredentialsRequest struct {
+ SnNumber string `json:"snNumber"`
+ Account string `json:"account"`
+}
+
+type CredentialsResponse struct {
+ Success bool `json:"success"`
+ Data struct {
+ AppKey string `json:"appKey"`
+ Secret string `json:"secret"`
+ MqttUrl string `json:"mqttUrl"`
+ Port int `json:"port"`
+ }
+ Msg string `json:"msg"`
+}
+
+type Payload struct {
+ *Command
+ *Data
+}
+
+type Command struct {
+ CommandTopic string `json:"command_topic"`
+ DeviceClass string `json:"device_class"`
+ Name string `json:"name"`
+ PayloadOff bool `json:"payload_off"`
+ PayloadOn bool `json:"payload_on"`
+ StateOff bool `json:"state_off"`
+ StateOn bool `json:"state_on"`
+ StateTopic string `json:"state_topic"`
+ UniqueId string `json:"unique_id"`
+ UnitOfMeasurement string `json:"unit_of_measurement"`
+ ValueTemplate string `json:"value_template"`
+}
+
+type Data struct {
+ AcMode int `json:"acMode"` // 1,
+ BuzzerSwitch bool `json:"buzzerSwitch"` // false,
+ ElectricLevel int `json:"electricLevel"` // 7,
+ GridInputPower int `json:"gridInputPower"` // 99,
+ HeatState int `json:"heatState"` // 0,
+ HubState int `json:"hubState"` // 0,
+ HyperTmp int `json:"hyperTmp"` // 2981,
+ InputLimit int `json:"inputLimit"` // 100,
+ InverseMaxPower int `json:"inverseMaxPower"` // 1200,
+ MasterSwitch bool `json:"masterSwitch"` // true,
+ OutputLimit int `json:"outputLimit"` // 0,
+ OutputPackPower int `json:"outputPackPower"` // 70,
+ OutputHomePower int `json:"outputHomePower"` // 70,
+ PackNum int `json:"packNum"` // 1,
+ PackState int `json:"packState"` // 0,
+ RemainInputTime int `json:"remainInputTime"` // 59940,
+ RemainOutTime int `json:"remainOutTime"` // 59940,
+ Sn string `json:"sn"` // "EE1LH",
+ SocSet int `json:"socSet"` // 1000,
+ SolarInputPower int `json:"solarInputPower"` // 0,
+ WifiState bool `json:"wifiState"` // true
+}
diff --git a/package-lock.json b/package-lock.json
index feb20ed3c1..ebbecd6300 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,7 +21,7 @@
"@vue/eslint-config-prettier": "^10.1.0",
"@vue/eslint-config-typescript": "^14.1.3",
"@vue/test-utils": "^2.2.7",
- "@vue/tsconfig": "^0.5.1",
+ "@vue/tsconfig": "^0.6.0",
"axios": "^1.7.4",
"body-parser": "^1.20.2",
"bootstrap": "^5.2.2",
@@ -84,9 +84,9 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz",
- "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.25.9",
@@ -98,9 +98,9 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz",
- "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz",
+ "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -136,22 +136,13 @@
"url": "https://opencollective.com/babel"
}
},
- "node_modules/@babel/core/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/@babel/generator": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz",
- "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz",
+ "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.26.0",
+ "@babel/parser": "^7.26.2",
"@babel/types": "^7.26.0",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
@@ -202,15 +193,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/@babel/helper-create-class-features-plugin": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz",
@@ -232,15 +214,6 @@
"@babel/core": "^7.0.0"
}
},
- "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/@babel/helper-create-regexp-features-plugin": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz",
@@ -258,19 +231,10 @@
"@babel/core": "^7.0.0"
}
},
- "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/@babel/helper-define-polyfill-provider": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz",
- "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==",
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz",
+ "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==",
"license": "MIT",
"dependencies": {
"@babel/helper-compilation-targets": "^7.22.6",
@@ -462,9 +426,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.26.1",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz",
- "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
+ "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.26.0"
@@ -1494,15 +1458,6 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/preset-env/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/@babel/preset-modules": {
"version": "0.1.6-no-external-plugins",
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
@@ -1649,9 +1604,9 @@
}
},
"node_modules/@codemirror/view": {
- "version": "6.34.1",
- "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.34.1.tgz",
- "integrity": "sha512-t1zK/l9UiRqwUNPm+pdIT0qzJlzuVckbTEMVNFhfWkGiBQClstzg+78vedCvLSX0xJEZ6lwZbPpnljL7L6iwMQ==",
+ "version": "6.34.3",
+ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.34.3.tgz",
+ "integrity": "sha512-Ph5d+u8DxIeSgssXEakaakImkzBV4+slwIbcxl9oc9evexJhImeu/G8TK7+zp+IFK9KuJ0BdSn6kTBJeH2CHvA==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.4.0",
@@ -2046,18 +2001,18 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.11.2",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.2.tgz",
- "integrity": "sha512-2WwyTYNVaMNUWPZTOJdkax9iqTdirrApgTbk+Qoq5EPX6myqZvG8QGFRgdKmkjKVG6/G/a565vpPauHk0+hpBA==",
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
"license": "MIT",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/config-array": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz",
- "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==",
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz",
+ "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==",
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^2.1.4",
@@ -2068,41 +2023,19 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@eslint/config-array/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/config-array/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/@eslint/core": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz",
- "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==",
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz",
+ "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==",
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/eslintrc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
- "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz",
+ "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==",
"license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
@@ -2122,45 +2055,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz",
- "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==",
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/espree": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz",
- "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "acorn": "^8.12.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
"node_modules/@eslint/eslintrc/node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
@@ -2173,22 +2067,10 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@eslint/eslintrc/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/@eslint/js": {
- "version": "9.13.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz",
- "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==",
+ "version": "9.15.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz",
+ "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==",
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2204,9 +2086,9 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz",
- "integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==",
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz",
+ "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==",
"license": "Apache-2.0",
"dependencies": {
"levn": "^0.4.1"
@@ -2356,27 +2238,40 @@
"license": "MIT"
},
"node_modules/@humanfs/core": {
- "version": "0.19.0",
- "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz",
- "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==",
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/node": {
- "version": "0.16.5",
- "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz",
- "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==",
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
"license": "Apache-2.0",
"dependencies": {
- "@humanfs/core": "^0.19.0",
+ "@humanfs/core": "^0.19.1",
"@humanwhocodes/retry": "^0.3.0"
},
"engines": {
"node": ">=18.18.0"
}
},
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -2391,9 +2286,9 @@
}
},
"node_modules/@humanwhocodes/retry": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
- "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz",
+ "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==",
"license": "Apache-2.0",
"engines": {
"node": ">=18.18"
@@ -2464,33 +2359,6 @@
"node": ">=12"
}
},
- "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
- "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
@@ -2667,12 +2535,12 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.48.2",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz",
- "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==",
+ "version": "1.49.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0.tgz",
+ "integrity": "sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==",
"license": "Apache-2.0",
"dependencies": {
- "playwright": "1.48.2"
+ "playwright": "1.49.0"
},
"bin": {
"playwright": "cli.js"
@@ -2732,9 +2600,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
- "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.3.tgz",
+ "integrity": "sha512-EzxVSkIvCFxUd4Mgm4xR9YXrcp976qVaHnqom/Tgm+vU79k4vV4eYTjmRvGfeoW8m9LVcsAy/lGjcgVegKEhLQ==",
"cpu": [
"arm"
],
@@ -2745,9 +2613,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz",
- "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.3.tgz",
+ "integrity": "sha512-LJc5pDf1wjlt9o/Giaw9Ofl+k/vLUaYsE2zeQGH85giX2F+wn/Cg8b3c5CDP3qmVmeO5NzwVUzQQxwZvC2eQKw==",
"cpu": [
"arm64"
],
@@ -2758,9 +2626,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
- "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.3.tgz",
+ "integrity": "sha512-OuRysZ1Mt7wpWJ+aYKblVbJWtVn3Cy52h8nLuNSzTqSesYw1EuN6wKp5NW/4eSre3mp12gqFRXOKTcN3AI3LqA==",
"cpu": [
"arm64"
],
@@ -2771,9 +2639,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz",
- "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.3.tgz",
+ "integrity": "sha512-xW//zjJMlJs2sOrCmXdB4d0uiilZsOdlGQIC/jjmMWT47lkLLoB1nsNhPUcnoqyi5YR6I4h+FjBpILxbEy8JRg==",
"cpu": [
"x64"
],
@@ -2783,10 +2651,36 @@
"darwin"
]
},
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.3.tgz",
+ "integrity": "sha512-58E0tIcwZ+12nK1WiLzHOD8I0d0kdrY/+o7yFVPRHuVGY3twBwzwDdTIBGRxLmyjciMYl1B/U515GJy+yn46qw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.3.tgz",
+ "integrity": "sha512-78fohrpcVwTLxg1ZzBMlwEimoAJmY6B+5TsyAZ3Vok7YabRBUvjYTsRXPTjGEvv/mfgVBepbW28OlMEz4w8wGA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz",
- "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.3.tgz",
+ "integrity": "sha512-h2Ay79YFXyQi+QZKo3ISZDyKaVD7uUvukEHTOft7kh00WF9mxAaxZsNs3o/eukbeKuH35jBvQqrT61fzKfAB/Q==",
"cpu": [
"arm"
],
@@ -2797,9 +2691,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz",
- "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.3.tgz",
+ "integrity": "sha512-Sv2GWmrJfRY57urktVLQ0VKZjNZGogVtASAgosDZ1aUB+ykPxSi3X1nWORL5Jk0sTIIwQiPH7iE3BMi9zGWfkg==",
"cpu": [
"arm"
],
@@ -2810,9 +2704,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz",
- "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.3.tgz",
+ "integrity": "sha512-FPoJBLsPW2bDNWjSrwNuTPUt30VnfM8GPGRoLCYKZpPx0xiIEdFip3dH6CqgoT0RnoGXptaNziM0WlKgBc+OWQ==",
"cpu": [
"arm64"
],
@@ -2823,9 +2717,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz",
- "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.3.tgz",
+ "integrity": "sha512-TKxiOvBorYq4sUpA0JT+Fkh+l+G9DScnG5Dqx7wiiqVMiRSkzTclP35pE6eQQYjP4Gc8yEkJGea6rz4qyWhp3g==",
"cpu": [
"arm64"
],
@@ -2836,9 +2730,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz",
- "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.3.tgz",
+ "integrity": "sha512-v2M/mPvVUKVOKITa0oCFksnQQ/TqGrT+yD0184/cWHIu0LoIuYHwox0Pm3ccXEz8cEQDLk6FPKd1CCm+PlsISw==",
"cpu": [
"ppc64"
],
@@ -2849,9 +2743,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz",
- "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.3.tgz",
+ "integrity": "sha512-LdrI4Yocb1a/tFVkzmOE5WyYRgEBOyEhWYJe4gsDWDiwnjYKjNs7PS6SGlTDB7maOHF4kxevsuNBl2iOcj3b4A==",
"cpu": [
"riscv64"
],
@@ -2862,9 +2756,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz",
- "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.3.tgz",
+ "integrity": "sha512-d4wVu6SXij/jyiwPvI6C4KxdGzuZOvJ6y9VfrcleHTwo68fl8vZC5ZYHsCVPUi4tndCfMlFniWgwonQ5CUpQcA==",
"cpu": [
"s390x"
],
@@ -2875,9 +2769,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz",
- "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.3.tgz",
+ "integrity": "sha512-/6bn6pp1fsCGEY5n3yajmzZQAh+mW4QPItbiWxs69zskBzJuheb3tNynEjL+mKOsUSFK11X4LYF2BwwXnzWleA==",
"cpu": [
"x64"
],
@@ -2888,9 +2782,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz",
- "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.3.tgz",
+ "integrity": "sha512-nBXOfJds8OzUT1qUreT/en3eyOXd2EH5b0wr2bVB5999qHdGKkzGzIyKYaKj02lXk6wpN71ltLIaQpu58YFBoQ==",
"cpu": [
"x64"
],
@@ -2901,9 +2795,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz",
- "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.3.tgz",
+ "integrity": "sha512-ogfbEVQgIZOz5WPWXF2HVb6En+kWzScuxJo/WdQTqEgeyGkaa2ui5sQav9Zkr7bnNCLK48uxmmK0TySm22eiuw==",
"cpu": [
"arm64"
],
@@ -2914,9 +2808,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz",
- "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.3.tgz",
+ "integrity": "sha512-ecE36ZBMLINqiTtSNQ1vzWc5pXLQHlf/oqGp/bSbi7iedcjcNb6QbCBNG73Euyy2C+l/fn8qKWEwxr+0SSfs3w==",
"cpu": [
"ia32"
],
@@ -2927,9 +2821,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
- "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.3.tgz",
+ "integrity": "sha512-vliZLrDmYKyaUoMzEbMTg2JkerfBjn03KmAw9CykO0Zzkzoyd7o3iZNam/TpyWNjNT+Cz2iO3P9Smv2wgrR+Eg==",
"cpu": [
"x64"
],
@@ -3046,25 +2940,25 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "22.8.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.1.tgz",
- "integrity": "sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==",
+ "version": "22.9.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
+ "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.8"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz",
- "integrity": "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz",
+ "integrity": "sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==",
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.11.0",
- "@typescript-eslint/type-utils": "8.11.0",
- "@typescript-eslint/utils": "8.11.0",
- "@typescript-eslint/visitor-keys": "8.11.0",
+ "@typescript-eslint/scope-manager": "8.15.0",
+ "@typescript-eslint/type-utils": "8.15.0",
+ "@typescript-eslint/utils": "8.15.0",
+ "@typescript-eslint/visitor-keys": "8.15.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@@ -3088,15 +2982,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.11.0.tgz",
- "integrity": "sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.15.0.tgz",
+ "integrity": "sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==",
"license": "BSD-2-Clause",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.11.0",
- "@typescript-eslint/types": "8.11.0",
- "@typescript-eslint/typescript-estree": "8.11.0",
- "@typescript-eslint/visitor-keys": "8.11.0",
+ "@typescript-eslint/scope-manager": "8.15.0",
+ "@typescript-eslint/types": "8.15.0",
+ "@typescript-eslint/typescript-estree": "8.15.0",
+ "@typescript-eslint/visitor-keys": "8.15.0",
"debug": "^4.3.4"
},
"engines": {
@@ -3116,13 +3010,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz",
- "integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz",
+ "integrity": "sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==",
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.11.0",
- "@typescript-eslint/visitor-keys": "8.11.0"
+ "@typescript-eslint/types": "8.15.0",
+ "@typescript-eslint/visitor-keys": "8.15.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3133,13 +3027,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz",
- "integrity": "sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz",
+ "integrity": "sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==",
"license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.11.0",
- "@typescript-eslint/utils": "8.11.0",
+ "@typescript-eslint/typescript-estree": "8.15.0",
+ "@typescript-eslint/utils": "8.15.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
},
@@ -3150,6 +3044,9 @@
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
"peerDependenciesMeta": {
"typescript": {
"optional": true
@@ -3157,9 +3054,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz",
- "integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.15.0.tgz",
+ "integrity": "sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==",
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3170,13 +3067,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz",
- "integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz",
+ "integrity": "sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==",
"license": "BSD-2-Clause",
"dependencies": {
- "@typescript-eslint/types": "8.11.0",
- "@typescript-eslint/visitor-keys": "8.11.0",
+ "@typescript-eslint/types": "8.15.0",
+ "@typescript-eslint/visitor-keys": "8.15.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -3197,16 +3094,52 @@
}
}
},
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/@typescript-eslint/utils": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz",
- "integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.15.0.tgz",
+ "integrity": "sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==",
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.11.0",
- "@typescript-eslint/types": "8.11.0",
- "@typescript-eslint/typescript-estree": "8.11.0"
+ "@typescript-eslint/scope-manager": "8.15.0",
+ "@typescript-eslint/types": "8.15.0",
+ "@typescript-eslint/typescript-estree": "8.15.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3217,16 +3150,21 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz",
- "integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz",
+ "integrity": "sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==",
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.11.0",
- "eslint-visitor-keys": "^3.4.3"
+ "@typescript-eslint/types": "8.15.0",
+ "eslint-visitor-keys": "^4.2.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3236,23 +3174,35 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/@unhead/dom": {
- "version": "1.11.10",
- "resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.11.10.tgz",
- "integrity": "sha512-nL1mdRzYVATZIYauK15zOI2YyM3YxCLfhbTqljEjDFJeiJUzTTi+a//5FHiUk84ewSucFnrwHNey/pEXFlyY1A==",
+ "version": "1.11.11",
+ "resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.11.11.tgz",
+ "integrity": "sha512-4YwziCH5CmjvUzSGdZ4Klj6BqhLSTNZooA9kt47yDxj4Qw9uHqVnXwWWupYsVdIYPNsw1tR2AkHveg82y1Fn3A==",
"license": "MIT",
"dependencies": {
- "@unhead/schema": "1.11.10",
- "@unhead/shared": "1.11.10"
+ "@unhead/schema": "1.11.11",
+ "@unhead/shared": "1.11.11"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
}
},
"node_modules/@unhead/schema": {
- "version": "1.11.10",
- "resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.11.10.tgz",
- "integrity": "sha512-lXh7cm5XtFaw3gc+ZVXTSfIHXiBpAywbjtEiOsz5TR4GxOjj2rtfOAl4C3Difk1yupP6L2otYmOZdn/i8EXSJg==",
+ "version": "1.11.11",
+ "resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.11.11.tgz",
+ "integrity": "sha512-xSGsWHPBYcMV/ckQeImbrVu6ddeRnrdDCgXUKv3xIjGBY+ob/96V80lGX8FKWh8GwdFSwhblISObKlDAt5K9ZQ==",
"license": "MIT",
"dependencies": {
"hookable": "^5.5.3",
@@ -3263,28 +3213,28 @@
}
},
"node_modules/@unhead/shared": {
- "version": "1.11.10",
- "resolved": "https://registry.npmjs.org/@unhead/shared/-/shared-1.11.10.tgz",
- "integrity": "sha512-YQgZcOyo1id7drUeDPGn0R83pirvIcV+Car3/m7ZfCLL1Syab6uXmRckVRd69yVbUL4eirIm9IzzmvzM/OuGuw==",
+ "version": "1.11.11",
+ "resolved": "https://registry.npmjs.org/@unhead/shared/-/shared-1.11.11.tgz",
+ "integrity": "sha512-RfdvUskPn90ipO+PmR98jKZ8Lsx1uuzscOenO5xcrMrtWGhlLWaEBIrbvFOvX5PZ/u8/VNMJChTXGDUjEtHmlg==",
"license": "MIT",
"dependencies": {
- "@unhead/schema": "1.11.10"
+ "@unhead/schema": "1.11.11"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
}
},
"node_modules/@unhead/vue": {
- "version": "1.11.10",
- "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-1.11.10.tgz",
- "integrity": "sha512-v6ddp4YEQCNILhYrx37Yt0GKRIFeTrb3VSmTbjh+URT+ua1mwgmNFTfl2ZldtTtri3tEkwSG1/5wLRq20ma70g==",
+ "version": "1.11.11",
+ "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-1.11.11.tgz",
+ "integrity": "sha512-AxsHHauZ+w0m2irwDHqkc3GdNChMLBtolk8CN3IAZM6vTwH0EbPXlFCFcIk4WwkH0opG+R2GlKTThr5H0HLm7g==",
"license": "MIT",
"dependencies": {
- "@unhead/schema": "1.11.10",
- "@unhead/shared": "1.11.10",
+ "@unhead/schema": "1.11.11",
+ "@unhead/shared": "1.11.11",
"defu": "^6.1.4",
"hookable": "^5.5.3",
- "unhead": "1.11.10"
+ "unhead": "1.11.11"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
@@ -3320,9 +3270,9 @@
}
},
"node_modules/@vitejs/plugin-vue": {
- "version": "5.1.4",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz",
- "integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.0.tgz",
+ "integrity": "sha512-7n7KdUEtx/7Yl7I/WVAMZ1bEb0eVvXF3ummWTeLcs/9gvo9pJhuLdouSXGjdZ/MKD1acf1I272+X0RMua4/R3g==",
"license": "MIT",
"engines": {
"node": "^18.0.0 || >=20.0.0"
@@ -3333,14 +3283,14 @@
}
},
"node_modules/@vitest/expect": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.3.tgz",
- "integrity": "sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==",
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.5.tgz",
+ "integrity": "sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q==",
"license": "MIT",
"dependencies": {
- "@vitest/spy": "2.1.3",
- "@vitest/utils": "2.1.3",
- "chai": "^5.1.1",
+ "@vitest/spy": "2.1.5",
+ "@vitest/utils": "2.1.5",
+ "chai": "^5.1.2",
"tinyrainbow": "^1.2.0"
},
"funding": {
@@ -3348,21 +3298,20 @@
}
},
"node_modules/@vitest/mocker": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.3.tgz",
- "integrity": "sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==",
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.5.tgz",
+ "integrity": "sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ==",
"license": "MIT",
"dependencies": {
- "@vitest/spy": "2.1.3",
+ "@vitest/spy": "2.1.5",
"estree-walker": "^3.0.3",
- "magic-string": "^0.30.11"
+ "magic-string": "^0.30.12"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
- "@vitest/spy": "2.1.3",
- "msw": "^2.3.5",
+ "msw": "^2.4.9",
"vite": "^5.0.0"
},
"peerDependenciesMeta": {
@@ -3384,9 +3333,9 @@
}
},
"node_modules/@vitest/pretty-format": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz",
- "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==",
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.5.tgz",
+ "integrity": "sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==",
"license": "MIT",
"dependencies": {
"tinyrainbow": "^1.2.0"
@@ -3396,12 +3345,12 @@
}
},
"node_modules/@vitest/runner": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.3.tgz",
- "integrity": "sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==",
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.5.tgz",
+ "integrity": "sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g==",
"license": "MIT",
"dependencies": {
- "@vitest/utils": "2.1.3",
+ "@vitest/utils": "2.1.5",
"pathe": "^1.1.2"
},
"funding": {
@@ -3409,13 +3358,13 @@
}
},
"node_modules/@vitest/snapshot": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.3.tgz",
- "integrity": "sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==",
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.5.tgz",
+ "integrity": "sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg==",
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "2.1.3",
- "magic-string": "^0.30.11",
+ "@vitest/pretty-format": "2.1.5",
+ "magic-string": "^0.30.12",
"pathe": "^1.1.2"
},
"funding": {
@@ -3423,25 +3372,25 @@
}
},
"node_modules/@vitest/spy": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.3.tgz",
- "integrity": "sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==",
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.5.tgz",
+ "integrity": "sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==",
"license": "MIT",
"dependencies": {
- "tinyspy": "^3.0.0"
+ "tinyspy": "^3.0.2"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/utils": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.3.tgz",
- "integrity": "sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==",
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.5.tgz",
+ "integrity": "sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==",
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "2.1.3",
- "loupe": "^3.1.1",
+ "@vitest/pretty-format": "2.1.5",
+ "loupe": "^3.1.2",
"tinyrainbow": "^1.2.0"
},
"funding": {
@@ -3449,53 +3398,53 @@
}
},
"node_modules/@vue/compiler-core": {
- "version": "3.5.12",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz",
- "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
+ "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.25.3",
- "@vue/shared": "3.5.12",
+ "@vue/shared": "3.5.13",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-dom": {
- "version": "3.5.12",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz",
- "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
+ "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-core": "3.5.12",
- "@vue/shared": "3.5.12"
+ "@vue/compiler-core": "3.5.13",
+ "@vue/shared": "3.5.13"
}
},
"node_modules/@vue/compiler-sfc": {
- "version": "3.5.12",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz",
- "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
+ "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.25.3",
- "@vue/compiler-core": "3.5.12",
- "@vue/compiler-dom": "3.5.12",
- "@vue/compiler-ssr": "3.5.12",
- "@vue/shared": "3.5.12",
+ "@vue/compiler-core": "3.5.13",
+ "@vue/compiler-dom": "3.5.13",
+ "@vue/compiler-ssr": "3.5.13",
+ "@vue/shared": "3.5.13",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.11",
- "postcss": "^8.4.47",
+ "postcss": "^8.4.48",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-ssr": {
- "version": "3.5.12",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz",
- "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
+ "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.12",
- "@vue/shared": "3.5.12"
+ "@vue/compiler-dom": "3.5.13",
+ "@vue/shared": "3.5.13"
}
},
"node_modules/@vue/devtools-api": {
@@ -3544,53 +3493,53 @@
}
},
"node_modules/@vue/reactivity": {
- "version": "3.5.12",
- "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz",
- "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
+ "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
"license": "MIT",
"dependencies": {
- "@vue/shared": "3.5.12"
+ "@vue/shared": "3.5.13"
}
},
"node_modules/@vue/runtime-core": {
- "version": "3.5.12",
- "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.12.tgz",
- "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
+ "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
"license": "MIT",
"dependencies": {
- "@vue/reactivity": "3.5.12",
- "@vue/shared": "3.5.12"
+ "@vue/reactivity": "3.5.13",
+ "@vue/shared": "3.5.13"
}
},
"node_modules/@vue/runtime-dom": {
- "version": "3.5.12",
- "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz",
- "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
+ "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
"license": "MIT",
"dependencies": {
- "@vue/reactivity": "3.5.12",
- "@vue/runtime-core": "3.5.12",
- "@vue/shared": "3.5.12",
+ "@vue/reactivity": "3.5.13",
+ "@vue/runtime-core": "3.5.13",
+ "@vue/shared": "3.5.13",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
- "version": "3.5.12",
- "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.12.tgz",
- "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
+ "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-ssr": "3.5.12",
- "@vue/shared": "3.5.12"
+ "@vue/compiler-ssr": "3.5.13",
+ "@vue/shared": "3.5.13"
},
"peerDependencies": {
- "vue": "3.5.12"
+ "vue": "3.5.13"
}
},
"node_modules/@vue/shared": {
- "version": "3.5.12",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz",
- "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
+ "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==",
"license": "MIT"
},
"node_modules/@vue/test-utils": {
@@ -3604,10 +3553,22 @@
}
},
"node_modules/@vue/tsconfig": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.5.1.tgz",
- "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
- "license": "MIT"
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.6.0.tgz",
+ "integrity": "sha512-MHXNd6lzugsEHvuA6l1GqrF5jROqUon8sP/HInLPnthJiYvB0VvpHMywg7em1dBZfFZNBSkR68qH37zOdRHmCw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "typescript": "5.x",
+ "vue": "^3.3.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ },
+ "vue": {
+ "optional": true
+ }
+ }
},
"node_modules/abab": {
"version": "2.0.6",
@@ -3626,9 +3587,9 @@
}
},
"node_modules/acorn": {
- "version": "8.13.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz",
- "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==",
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -3697,12 +3658,15 @@
}
},
"node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"license": "MIT",
"engines": {
- "node": ">=8"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/ansi-styles": {
@@ -3895,28 +3859,19 @@
}
},
"node_modules/babel-plugin-polyfill-corejs2": {
- "version": "0.4.11",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz",
- "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==",
+ "version": "0.4.12",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz",
+ "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==",
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.22.6",
- "@babel/helper-define-polyfill-provider": "^0.6.2",
+ "@babel/helper-define-polyfill-provider": "^0.6.3",
"semver": "^6.3.1"
},
"peerDependencies": {
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
- "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/babel-plugin-polyfill-corejs3": {
"version": "0.10.6",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
@@ -3931,12 +3886,12 @@
}
},
"node_modules/babel-plugin-polyfill-regenerator": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz",
- "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==",
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz",
+ "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==",
"license": "MIT",
"dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.6.2"
+ "@babel/helper-define-polyfill-provider": "^0.6.3"
},
"peerDependencies": {
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
@@ -4034,12 +3989,13 @@
}
},
"node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"license": "MIT",
"dependencies": {
- "balanced-match": "^1.0.0"
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
}
},
"node_modules/braces": {
@@ -4168,9 +4124,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001671",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001671.tgz",
- "integrity": "sha512-jocyVaSSfXg2faluE6hrWkMgDOiULBMca4QLtDT39hw1YxaIPHWc1CcTCKkPmHgGH6tKji6ZNbMSmUAvENf2/A==",
+ "version": "1.0.30001680",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz",
+ "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==",
"funding": [
{
"type": "opencollective",
@@ -4261,9 +4217,9 @@
}
},
"node_modules/chart.js": {
- "version": "4.4.5",
- "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.5.tgz",
- "integrity": "sha512-CVVjg1RYTJV9OCC8WeJPMx8gsV8K6WIyIEQUE3ui4AR9Hfgls9URri6Ja3hyMVBbTF8Q2KFa19PE815gWcWhng==",
+ "version": "4.4.6",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz",
+ "integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==",
"license": "MIT",
"dependencies": {
"@kurkle/color": "^0.3.0"
@@ -4319,6 +4275,15 @@
"node": ">=12"
}
},
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/cliui/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -4339,6 +4304,18 @@
"node": ">=8"
}
},
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/cliui/node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -4474,9 +4451,9 @@
"license": "MIT"
},
"node_modules/core-js": {
- "version": "3.38.1",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz",
- "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==",
+ "version": "3.39.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz",
+ "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==",
"hasInstallScript": true,
"license": "MIT",
"funding": {
@@ -4485,12 +4462,12 @@
}
},
"node_modules/core-js-compat": {
- "version": "3.38.1",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz",
- "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==",
+ "version": "3.39.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz",
+ "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==",
"license": "MIT",
"dependencies": {
- "browserslist": "^4.23.3"
+ "browserslist": "^4.24.2"
},
"funding": {
"type": "opencollective",
@@ -4510,9 +4487,9 @@
"license": "MIT"
},
"node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -4763,6 +4740,18 @@
"node": ">=8"
}
},
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/domexception": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
@@ -4810,6 +4799,15 @@
"node": ">=14"
}
},
+ "node_modules/editorconfig/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
"node_modules/editorconfig/node_modules/minimatch": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz",
@@ -4825,6 +4823,18 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/editorconfig/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -4832,9 +4842,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.47",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz",
- "integrity": "sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==",
+ "version": "1.5.63",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.63.tgz",
+ "integrity": "sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA==",
"license": "ISC"
},
"node_modules/emoji-regex": {
@@ -4865,9 +4875,9 @@
}
},
"node_modules/es-abstract": {
- "version": "1.23.3",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
- "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
+ "version": "1.23.5",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz",
+ "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==",
"license": "MIT",
"dependencies": {
"array-buffer-byte-length": "^1.0.1",
@@ -4885,7 +4895,7 @@
"function.prototype.name": "^1.1.6",
"get-intrinsic": "^1.2.4",
"get-symbol-description": "^1.0.2",
- "globalthis": "^1.0.3",
+ "globalthis": "^1.0.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2",
"has-proto": "^1.0.3",
@@ -4901,10 +4911,10 @@
"is-string": "^1.0.7",
"is-typed-array": "^1.1.13",
"is-weakref": "^1.0.2",
- "object-inspect": "^1.13.1",
+ "object-inspect": "^1.13.3",
"object-keys": "^1.1.1",
"object.assign": "^4.1.5",
- "regexp.prototype.flags": "^1.5.2",
+ "regexp.prototype.flags": "^1.5.3",
"safe-array-concat": "^1.1.2",
"safe-regex-test": "^1.0.3",
"string.prototype.trim": "^1.2.9",
@@ -4945,6 +4955,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/es-module-lexer": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
+ "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
+ "license": "MIT"
+ },
"node_modules/es-object-atoms": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
@@ -5084,31 +5100,31 @@
}
},
"node_modules/eslint": {
- "version": "9.13.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz",
- "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==",
+ "version": "9.15.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz",
+ "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==",
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.11.0",
- "@eslint/config-array": "^0.18.0",
- "@eslint/core": "^0.7.0",
- "@eslint/eslintrc": "^3.1.0",
- "@eslint/js": "9.13.0",
- "@eslint/plugin-kit": "^0.2.0",
- "@humanfs/node": "^0.16.5",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.19.0",
+ "@eslint/core": "^0.9.0",
+ "@eslint/eslintrc": "^3.2.0",
+ "@eslint/js": "9.15.0",
+ "@eslint/plugin-kit": "^0.2.3",
+ "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.3.1",
+ "@humanwhocodes/retry": "^0.4.1",
"@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
+ "cross-spawn": "^7.0.5",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.1.0",
- "eslint-visitor-keys": "^4.1.0",
- "espree": "^10.2.0",
+ "eslint-scope": "^8.2.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -5122,8 +5138,7 @@
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
- "optionator": "^0.9.3",
- "text-table": "^0.2.0"
+ "optionator": "^0.9.3"
},
"bin": {
"eslint": "bin/eslint.js"
@@ -5253,16 +5268,6 @@
"eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
}
},
- "node_modules/eslint-plugin-import/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/eslint-plugin-import/node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
@@ -5272,39 +5277,6 @@
"ms": "^2.1.1"
}
},
- "node_modules/eslint-plugin-import/node_modules/doctrine": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
- "license": "Apache-2.0",
- "dependencies": {
- "esutils": "^2.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/eslint-plugin-import/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/eslint-plugin-import/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/eslint-plugin-node": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
@@ -5325,37 +5297,6 @@
"eslint": ">=5.16.0"
}
},
- "node_modules/eslint-plugin-node/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/eslint-plugin-node/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/eslint-plugin-node/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/eslint-plugin-prettier": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
@@ -5402,9 +5343,9 @@
}
},
"node_modules/eslint-plugin-vue": {
- "version": "9.29.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.29.1.tgz",
- "integrity": "sha512-MH/MbVae4HV/tM8gKAVWMPJbYgW04CK7SuzYRrlNERpxbO0P3+Zdsa2oAcFBW6xNu7W6lIkGOsFAMCRTYmrlWQ==",
+ "version": "9.31.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.31.0.tgz",
+ "integrity": "sha512-aYMUCgivhz1o4tLkRHj5oq9YgYPM4/EJc0M7TAKRLCUA5OYxRLAhYEVD2nLtTwLyixEFI+/QXSvKU9ESZFgqjQ==",
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
@@ -5438,17 +5379,29 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/eslint-plugin-vue/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
+ "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
"license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -5490,54 +5443,11 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/eslint/node_modules/eslint-scope": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz",
- "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
"node_modules/eslint/node_modules/eslint-visitor-keys": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz",
- "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==",
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint/node_modules/espree": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz",
- "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "acorn": "^8.12.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.1.0"
- },
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -5557,30 +5467,30 @@
"node": ">=10.13.0"
}
},
- "node_modules/eslint/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
"license": "BSD-2-Clause",
"dependencies": {
- "acorn": "^8.9.0",
+ "acorn": "^8.14.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
+ "eslint-visitor-keys": "^4.2.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -5647,6 +5557,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/expect-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz",
+ "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
@@ -5807,9 +5726,9 @@
}
},
"node_modules/flatted": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
- "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
+ "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
"license": "ISC"
},
"node_modules/flexsearch": {
@@ -6027,10 +5946,34 @@
"node": ">= 6"
}
},
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/globals": {
- "version": "15.11.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz",
- "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==",
+ "version": "15.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz",
+ "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -6136,9 +6079,9 @@
}
},
"node_modules/happy-dom": {
- "version": "15.7.4",
- "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.7.4.tgz",
- "integrity": "sha512-r1vadDYGMtsHAAsqhDuk4IpPvr6N8MGKy5ntBo7tSdim+pWDxus2PNqOcOt8LuDZ4t3KJHE+gCuzupcx/GKnyQ==",
+ "version": "15.11.6",
+ "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.11.6.tgz",
+ "integrity": "sha512-elX7iUTu+5+3b2+NGQc0L3eWyq9jKhuJJ4GpOMxxT/c2pg9O3L5H3ty2VECX0XXZgRmmRqXyOK8brA2hDI6LsQ==",
"license": "MIT",
"dependencies": {
"entities": "^4.5.0",
@@ -7040,9 +6983,9 @@
}
},
"node_modules/magic-string": {
- "version": "0.30.12",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz",
- "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==",
+ "version": "0.30.13",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.13.tgz",
+ "integrity": "sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0"
@@ -7187,18 +7130,15 @@
}
},
"node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"license": "ISC",
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "brace-expansion": "^1.1.7"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "node": "*"
}
},
"node_modules/minimist": {
@@ -7220,14 +7160,14 @@
}
},
"node_modules/mlly": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz",
- "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==",
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz",
+ "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==",
"license": "MIT",
"dependencies": {
- "acorn": "^8.12.1",
+ "acorn": "^8.14.0",
"pathe": "^1.1.2",
- "pkg-types": "^1.2.0",
+ "pkg-types": "^1.2.1",
"ufo": "^1.5.4"
}
},
@@ -7345,9 +7285,9 @@
"license": "MIT"
},
"node_modules/object-inspect": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
- "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
+ "version": "1.13.3",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
+ "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -7537,9 +7477,9 @@
}
},
"node_modules/parse5": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz",
- "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==",
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
+ "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
"license": "MIT",
"dependencies": {
"entities": "^4.5.0"
@@ -7677,12 +7617,12 @@
}
},
"node_modules/playwright": {
- "version": "1.48.2",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz",
- "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==",
+ "version": "1.49.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0.tgz",
+ "integrity": "sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==",
"license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.48.2"
+ "playwright-core": "1.49.0"
},
"bin": {
"playwright": "cli.js"
@@ -7695,9 +7635,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.48.2",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz",
- "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==",
+ "version": "1.49.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0.tgz",
+ "integrity": "sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
@@ -7730,9 +7670,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.47",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
- "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+ "version": "8.4.49",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
+ "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"funding": [
{
"type": "opencollective",
@@ -7750,7 +7690,7 @@
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
- "picocolors": "^1.1.0",
+ "picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
@@ -7837,10 +7777,13 @@
"license": "MIT"
},
"node_modules/psl": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
- "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
- "license": "MIT"
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz",
+ "integrity": "sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA==",
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ }
},
"node_modules/punycode": {
"version": "2.3.1",
@@ -8006,9 +7949,9 @@
"license": "MIT"
},
"node_modules/regjsparser": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz",
- "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==",
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz",
+ "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==",
"license": "BSD-2-Clause",
"dependencies": {
"jsesc": "~3.0.2"
@@ -8069,9 +8012,9 @@
}
},
"node_modules/rollup": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
- "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.3.tgz",
+ "integrity": "sha512-SLsCOnlmGt9VoZ9Ek8yBK8tAdmPHeppkw+Xa7yDlCEhDTvwYei03JlWo1fdc7YTfLZ4tD8riJCUyAgTbszk1fQ==",
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.6"
@@ -8084,22 +8027,24 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.24.0",
- "@rollup/rollup-android-arm64": "4.24.0",
- "@rollup/rollup-darwin-arm64": "4.24.0",
- "@rollup/rollup-darwin-x64": "4.24.0",
- "@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
- "@rollup/rollup-linux-arm-musleabihf": "4.24.0",
- "@rollup/rollup-linux-arm64-gnu": "4.24.0",
- "@rollup/rollup-linux-arm64-musl": "4.24.0",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
- "@rollup/rollup-linux-riscv64-gnu": "4.24.0",
- "@rollup/rollup-linux-s390x-gnu": "4.24.0",
- "@rollup/rollup-linux-x64-gnu": "4.24.0",
- "@rollup/rollup-linux-x64-musl": "4.24.0",
- "@rollup/rollup-win32-arm64-msvc": "4.24.0",
- "@rollup/rollup-win32-ia32-msvc": "4.24.0",
- "@rollup/rollup-win32-x64-msvc": "4.24.0",
+ "@rollup/rollup-android-arm-eabi": "4.27.3",
+ "@rollup/rollup-android-arm64": "4.27.3",
+ "@rollup/rollup-darwin-arm64": "4.27.3",
+ "@rollup/rollup-darwin-x64": "4.27.3",
+ "@rollup/rollup-freebsd-arm64": "4.27.3",
+ "@rollup/rollup-freebsd-x64": "4.27.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.27.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.27.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.27.3",
+ "@rollup/rollup-linux-arm64-musl": "4.27.3",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.27.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.27.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.27.3",
+ "@rollup/rollup-linux-x64-gnu": "4.27.3",
+ "@rollup/rollup-linux-x64-musl": "4.27.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.27.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.27.3",
+ "@rollup/rollup-win32-x64-msvc": "4.27.3",
"fsevents": "~2.3.2"
}
},
@@ -8249,15 +8194,12 @@
}
},
"node_modules/semver": {
- "version": "7.6.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
- "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
}
},
"node_modules/sentence-case": {
@@ -8496,9 +8438,9 @@
}
},
"node_modules/std-env": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
- "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==",
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz",
+ "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==",
"license": "MIT"
},
"node_modules/string-width": {
@@ -8533,37 +8475,31 @@
"node": ">=8"
}
},
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/string-width-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
- "node_modules/string-width/node_modules/ansi-regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
- "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/string-width/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
- "ansi-regex": "^6.0.1"
+ "ansi-regex": "^5.0.1"
},
"engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ "node": ">=8"
}
},
"node_modules/string.prototype.trim": {
@@ -8616,15 +8552,18 @@
}
},
"node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"license": "MIT",
"dependencies": {
- "ansi-regex": "^5.0.1"
+ "ansi-regex": "^6.0.1"
},
"engines": {
- "node": ">=8"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/strip-ansi-cjs": {
@@ -8640,6 +8579,15 @@
"node": ">=8"
}
},
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
@@ -8754,12 +8702,6 @@
"license": "MIT",
"peer": true
},
- "node_modules/text-table": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
- "license": "MIT"
- },
"node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
@@ -8773,9 +8715,9 @@
"license": "MIT"
},
"node_modules/tinypool": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz",
- "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
+ "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==",
"license": "MIT",
"engines": {
"node": "^18.0.0 || >=20.0.0"
@@ -8881,9 +8823,9 @@
}
},
"node_modules/ts-api-utils": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
- "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz",
+ "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==",
"license": "MIT",
"engines": {
"node": ">=16"
@@ -8917,9 +8859,9 @@
}
},
"node_modules/tslib": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz",
- "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==",
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/type-check": {
@@ -9046,14 +8988,14 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.11.0.tgz",
- "integrity": "sha512-cBRGnW3FSlxaYwU8KfAewxFK5uzeOAp0l2KebIlPDOT5olVi65KDG/yjBooPBG0kGW/HLkoz1c/iuBFehcS3IA==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.15.0.tgz",
+ "integrity": "sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w==",
"license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.11.0",
- "@typescript-eslint/parser": "8.11.0",
- "@typescript-eslint/utils": "8.11.0"
+ "@typescript-eslint/eslint-plugin": "8.15.0",
+ "@typescript-eslint/parser": "8.15.0",
+ "@typescript-eslint/utils": "8.15.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -9062,6 +9004,9 @@
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
"peerDependenciesMeta": {
"typescript": {
"optional": true
@@ -9102,14 +9047,14 @@
"license": "MIT"
},
"node_modules/unhead": {
- "version": "1.11.10",
- "resolved": "https://registry.npmjs.org/unhead/-/unhead-1.11.10.tgz",
- "integrity": "sha512-hypXrAI47wE3wIhkze0RMPGAWcoo45Q1+XzdqLD/OnTCzjFXQrpuE4zBy8JRexyrqp+Ud2+nFTUNf/mjfFSymw==",
+ "version": "1.11.11",
+ "resolved": "https://registry.npmjs.org/unhead/-/unhead-1.11.11.tgz",
+ "integrity": "sha512-98tM2R8OWJhvS6uqTewkfIrsPqFU/VwnKpU2tVZ+jPXSWgWSLmM3K2Y2v5AEM4bZjmC/XH8pLVGzbqB7xzFI/Q==",
"license": "MIT",
"dependencies": {
- "@unhead/dom": "1.11.10",
- "@unhead/schema": "1.11.10",
- "@unhead/shared": "1.11.10",
+ "@unhead/dom": "1.11.11",
+ "@unhead/schema": "1.11.11",
+ "@unhead/shared": "1.11.11",
"hookable": "^5.5.3"
},
"funding": {
@@ -9257,9 +9202,9 @@
}
},
"node_modules/vite": {
- "version": "5.4.10",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz",
- "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==",
+ "version": "5.4.11",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
+ "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
"license": "MIT",
"dependencies": {
"esbuild": "^0.21.3",
@@ -9355,29 +9300,30 @@
}
},
"node_modules/vitest": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.3.tgz",
- "integrity": "sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==",
- "license": "MIT",
- "dependencies": {
- "@vitest/expect": "2.1.3",
- "@vitest/mocker": "2.1.3",
- "@vitest/pretty-format": "^2.1.3",
- "@vitest/runner": "2.1.3",
- "@vitest/snapshot": "2.1.3",
- "@vitest/spy": "2.1.3",
- "@vitest/utils": "2.1.3",
- "chai": "^5.1.1",
- "debug": "^4.3.6",
- "magic-string": "^0.30.11",
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.5.tgz",
+ "integrity": "sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "2.1.5",
+ "@vitest/mocker": "2.1.5",
+ "@vitest/pretty-format": "^2.1.5",
+ "@vitest/runner": "2.1.5",
+ "@vitest/snapshot": "2.1.5",
+ "@vitest/spy": "2.1.5",
+ "@vitest/utils": "2.1.5",
+ "chai": "^5.1.2",
+ "debug": "^4.3.7",
+ "expect-type": "^1.1.0",
+ "magic-string": "^0.30.12",
"pathe": "^1.1.2",
- "std-env": "^3.7.0",
+ "std-env": "^3.8.0",
"tinybench": "^2.9.0",
- "tinyexec": "^0.3.0",
- "tinypool": "^1.0.0",
+ "tinyexec": "^0.3.1",
+ "tinypool": "^1.0.1",
"tinyrainbow": "^1.2.0",
"vite": "^5.0.0",
- "vite-node": "2.1.3",
+ "vite-node": "2.1.5",
"why-is-node-running": "^2.3.0"
},
"bin": {
@@ -9392,8 +9338,8 @@
"peerDependencies": {
"@edge-runtime/vm": "*",
"@types/node": "^18.0.0 || >=20.0.0",
- "@vitest/browser": "2.1.3",
- "@vitest/ui": "2.1.3",
+ "@vitest/browser": "2.1.5",
+ "@vitest/ui": "2.1.5",
"happy-dom": "*",
"jsdom": "*"
},
@@ -9419,13 +9365,14 @@
}
},
"node_modules/vitest/node_modules/vite-node": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.3.tgz",
- "integrity": "sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==",
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.5.tgz",
+ "integrity": "sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==",
"license": "MIT",
"dependencies": {
"cac": "^6.7.14",
- "debug": "^4.3.6",
+ "debug": "^4.3.7",
+ "es-module-lexer": "^1.5.4",
"pathe": "^1.1.2",
"vite": "^5.0.0"
},
@@ -9440,16 +9387,16 @@
}
},
"node_modules/vue": {
- "version": "3.5.12",
- "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.12.tgz",
- "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
+ "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.12",
- "@vue/compiler-sfc": "3.5.12",
- "@vue/runtime-dom": "3.5.12",
- "@vue/server-renderer": "3.5.12",
- "@vue/shared": "3.5.12"
+ "@vue/compiler-dom": "3.5.13",
+ "@vue/compiler-sfc": "3.5.13",
+ "@vue/runtime-dom": "3.5.13",
+ "@vue/server-renderer": "3.5.13",
+ "@vue/shared": "3.5.13"
},
"peerDependencies": {
"typescript": "*"
@@ -9461,9 +9408,9 @@
}
},
"node_modules/vue-chartjs": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.1.tgz",
- "integrity": "sha512-rZjqcHBxKiHrBl0CIvcOlVEBwRhpWAVf6rDU3vUfa7HuSRmGtCslc0Oc8m16oAVuk0erzc1FCtH1VCriHsrz+A==",
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.2.tgz",
+ "integrity": "sha512-NrkbRRoYshbXbWqJkTN6InoDVwVb90C0R7eAVgMWcB9dPikbruaOoTFjFYHE/+tNPdIe6qdLCDjfjPHQ0fw4jw==",
"license": "MIT",
"peerDependencies": {
"chart.js": "^4.1.1",
@@ -9471,9 +9418,9 @@
}
},
"node_modules/vue-component-type-helpers": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.1.8.tgz",
- "integrity": "sha512-ii36gDzrYAfOQIkOlo44yceDdT5269gKmNGxf07Qx6seH2U50+tQ2ol02XLhYPmxrh6YabAsOdte8WDrpaO6Tw==",
+ "version": "2.1.10",
+ "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.1.10.tgz",
+ "integrity": "sha512-lfgdSLQKrUmADiSV6PbBvYgQ33KF3Ztv6gP85MfGaGaSGMTXORVaHT1EHfsqCgzRNBstPKYDmvAV9Do5CmJ07A==",
"license": "MIT"
},
"node_modules/vue-eslint-parser": {
@@ -9500,6 +9447,51 @@
"eslint": ">=6.0.0"
}
},
+ "node_modules/vue-eslint-parser/node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/vue-eslint-parser/node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/vue-eslint-parser/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/vue-hot-reload-api": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
@@ -9743,6 +9735,15 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -9763,16 +9764,16 @@
"node": ">=8"
}
},
- "node_modules/wrap-ansi/node_modules/ansi-regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
- "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
- "engines": {
- "node": ">=12"
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
},
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ "engines": {
+ "node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
@@ -9787,21 +9788,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/wrap-ansi/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
@@ -9880,6 +9866,15 @@
"node": ">=12"
}
},
+ "node_modules/yargs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/yargs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -9900,6 +9895,18 @@
"node": ">=8"
}
},
+ "node_modules/yargs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index 02ebafcb81..8309ca235a 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
"@vue/eslint-config-prettier": "^10.1.0",
"@vue/eslint-config-typescript": "^14.1.3",
"@vue/test-utils": "^2.2.7",
- "@vue/tsconfig": "^0.5.1",
+ "@vue/tsconfig": "^0.6.0",
"axios": "^1.7.4",
"body-parser": "^1.20.2",
"bootstrap": "^5.2.2",
diff --git a/packaging/docker/bin/entrypoint.sh b/packaging/docker/bin/entrypoint.sh
index ed4228b662..1fea90c3ed 100755
--- a/packaging/docker/bin/entrypoint.sh
+++ b/packaging/docker/bin/entrypoint.sh
@@ -7,9 +7,9 @@ HASSIO_OPTIONSFILE=/data/options.json
if [ -f ${HASSIO_OPTIONSFILE} ]; then
CONFIG=$(grep -o '"config_file": "[^"]*' ${HASSIO_OPTIONSFILE} | grep -o '[^"]*$')
echo "Using config file: ${CONFIG}"
-
+
SQLITE_FILE=$(grep -o '"sqlite_file": "[^"]*' ${HASSIO_OPTIONSFILE} | grep -o '[^"]*$')
-
+
if [ ! -f "${CONFIG}" ]; then
echo "Config not found. Please create a config under ${CONFIG}."
echo "For details see evcc documentation at https://github.com/evcc-io/evcc#readme."
@@ -23,7 +23,10 @@ if [ -f ${HASSIO_OPTIONSFILE} ]; then
fi
fi
else
- if [ "$1" = '"evcc"' ] || expr "$1" : '-*' > /dev/null; then
+ if [ "$1" = 'evcc' ]; then
+ shift
+ exec evcc "$@"
+ elif expr "$1" : '-.*' > /dev/null; then
exec evcc "$@"
else
exec "$@"
diff --git a/provider/script.go b/provider/script.go
index 7ed25cb63f..0a9f8355b3 100644
--- a/provider/script.go
+++ b/provider/script.go
@@ -126,14 +126,10 @@ func (p *Script) StringGetter() (func() (string, error), error) {
}, nil
}
-var _ SetIntProvider = (*Script)(nil)
-
-// IntSetter invokes script with parameter replaced by int value
-func (p *Script) IntSetter(param string) (func(int64) error, error) {
- // return func to access cached value
- return func(i int64) error {
+func scriptSetter[T any](p *Script, param string) (func(T) error, error) {
+ return func(val T) error {
cmd, err := util.ReplaceFormatted(p.script, map[string]interface{}{
- param: i,
+ param: val,
})
if err == nil {
@@ -144,20 +140,23 @@ func (p *Script) IntSetter(param string) (func(int64) error, error) {
}, nil
}
+var _ SetIntProvider = (*Script)(nil)
+
+// IntSetter invokes script with parameter replaced by int value
+func (p *Script) IntSetter(param string) (func(int64) error, error) {
+ return scriptSetter[int64](p, param)
+}
+
var _ SetBoolProvider = (*Script)(nil)
// BoolSetter invokes script with parameter replaced by bool value
func (p *Script) BoolSetter(param string) (func(bool) error, error) {
- // return func to access cached value
- return func(b bool) error {
- cmd, err := util.ReplaceFormatted(p.script, map[string]interface{}{
- param: b,
- })
+ return scriptSetter[bool](p, param)
+}
- if err == nil {
- _, err = p.exec(cmd)
- }
+var _ SetStringProvider = (*Script)(nil)
- return err
- }, nil
+// StringSetter returns a function that invokes a script with parameter by a string value
+func (p *Script) StringSetter(param string) (func(string) error, error) {
+ return scriptSetter[string](p, param)
}
diff --git a/push/push.go b/push/push.go
new file mode 100644
index 0000000000..f3dc810512
--- /dev/null
+++ b/push/push.go
@@ -0,0 +1,91 @@
+package push
+
+import (
+ "bytes"
+ "context"
+ "encoding/csv"
+ "encoding/json"
+ "strings"
+
+ "github.com/evcc-io/evcc/api"
+ "github.com/evcc-io/evcc/provider"
+ "github.com/evcc-io/evcc/util"
+)
+
+func init() {
+ registry.AddCtx(api.Custom, NewConfigurableFromConfig)
+}
+
+// NewConfigurableFromConfig creates Messenger from config
+func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}) (Messenger, error) {
+ var cc struct {
+ Send provider.Config
+ Encoding string
+ }
+
+ if err := util.DecodeOther(other, &cc); err != nil {
+ return nil, err
+ }
+
+ send, err := provider.NewStringSetterFromConfig(ctx, "send", cc.Send)
+ if err != nil {
+ return nil, err
+ }
+
+ return NewConfigurable(send, cc.Encoding)
+}
+
+// NewConfigurable creates a new Messenger
+func NewConfigurable(send func(string) error, encoding string) (*Push, error) {
+ m := &Push{
+ log: util.NewLogger("push"),
+ send: send,
+ encoding: strings.ToLower(encoding),
+ }
+ return m, nil
+}
+
+// Push is a configurable Messenger implementation
+type Push struct {
+ log *util.Logger
+ send func(string) error
+ encoding string
+}
+
+func (m *Push) csv(separator rune, title, msg string) string {
+ var b bytes.Buffer
+ ww := csv.NewWriter(&b)
+ ww.Comma = separator
+ _ = ww.Write([]string{title, msg})
+ ww.Flush()
+ return b.String()
+}
+
+// Send implements the Messenger interface
+func (m *Push) Send(title, msg string) {
+ var res string
+
+ switch m.encoding {
+ case "json":
+ b, _ := json.Marshal(struct {
+ Title string `json:"title,omitempty"`
+ Msg string `json:"msg"`
+ }{
+ Title: title,
+ Msg: msg,
+ })
+ res = string(b)
+ case "csv":
+ res = m.csv(',', title, msg)
+ case "tsv":
+ res = m.csv('\t', title, msg)
+ case "title":
+ res = title
+ default:
+ res = msg
+ }
+
+ if err := m.send(res); err != nil {
+ m.log.ERROR.Printf("send: %v", err)
+ }
+}
diff --git a/push/script.go b/push/script.go
deleted file mode 100644
index 367aafe507..0000000000
--- a/push/script.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package push
-
-import (
- "context"
- "errors"
- "os/exec"
- "strings"
- "time"
-
- "github.com/evcc-io/evcc/util"
- "github.com/evcc-io/evcc/util/request"
- "github.com/kballard/go-shellquote"
-)
-
-func init() {
- registry.Add("script", NewScriptFromConfig)
-}
-
-// Script implements shell script-based message service and setters
-type Script struct {
- log *util.Logger
- script string
- timeout time.Duration
-}
-
-// NewScriptFromConfig creates a Script messenger. Script execution is aborted after given timeout.
-func NewScriptFromConfig(other map[string]interface{}) (Messenger, error) {
- cc := struct {
- CmdLine string
- Timeout time.Duration
- }{
- Timeout: request.Timeout,
- }
-
- if err := util.DecodeOther(other, &cc); err != nil {
- return nil, err
- }
-
- s := &Script{
- log: util.NewLogger("script"),
- script: cc.CmdLine,
- timeout: cc.Timeout,
- }
-
- return s, nil
-}
-
-// Send calls the script
-func (m *Script) Send(title, msg string) {
- _, err := m.exec(m.script, title, msg)
- if err != nil {
- m.log.ERROR.Printf("exec: %v", err)
- }
-}
-
-func (m *Script) exec(script, title, msg string) (string, error) {
- args, err := shellquote.Split(script)
- if err != nil {
- return "", err
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), m.timeout)
- defer cancel()
-
- args = append(args, title, msg)
- cmd := exec.CommandContext(ctx, args[0], args[1:]...)
- b, err := cmd.Output()
-
- s := strings.TrimSpace(string(b))
-
- if err != nil {
- // use STDOUT if available
- var ee *exec.ExitError
- if errors.As(err, &ee) {
- s = strings.TrimSpace(string(ee.Stderr))
- }
-
- m.log.ERROR.Printf("%s: %s", strings.Join(args, " "), s)
- return "", err
- }
-
- m.log.DEBUG.Printf("%s: %s", strings.Join(args, " "), s)
-
- return s, nil
-}
diff --git a/server/http.go b/server/http.go
index 8ade922129..68265d695b 100644
--- a/server/http.go
+++ b/server/http.go
@@ -190,9 +190,8 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, valueChan chan<- util.Param)
}
// RegisterSystemHandler provides system level handlers
-func (s *HTTPd) RegisterSystemHandler(valueChan chan<- util.Param, cache *util.Cache, shutdown func()) {
+func (s *HTTPd) RegisterSystemHandler(valueChan chan<- util.Param, cache *util.Cache, auth auth.Auth, shutdown func()) {
router := s.Server.Handler.(*mux.Router)
- auth := auth.New()
// api
api := router.PathPrefix("/api").Subrouter()
diff --git a/server/http_auth.go b/server/http_auth.go
index 160bd2ebd2..0b51e67579 100644
--- a/server/http_auth.go
+++ b/server/http_auth.go
@@ -73,6 +73,11 @@ func jwtFromRequest(r *http.Request) string {
// authStatusHandler login status (true/false) based on jwt token. Error if admin password is not configured
func authStatusHandler(auth auth.Auth) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
+ if auth.Disabled() {
+ w.Write([]byte("true"))
+ return
+ }
+
if !auth.IsAdminPasswordConfigured() {
http.Error(w, "Not implemented", http.StatusNotImplemented)
return
@@ -130,6 +135,11 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) {
func ensureAuthHandler(auth auth.Auth) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if auth.Disabled() {
+ next.ServeHTTP(w, r)
+ return
+ }
+
// check jwt token
ok, err := auth.ValidateJwtToken(jwtFromRequest(r))
if !ok || err != nil {
diff --git a/server/mqtt.go b/server/mqtt.go
index a676c71b94..d9f4f6920b 100644
--- a/server/mqtt.go
+++ b/server/mqtt.go
@@ -173,19 +173,19 @@ func (m *MQTT) Listen(site site.API) error {
func (m *MQTT) listenSiteSetters(topic string, site site.API) error {
for _, s := range []setter{
- {"/bufferSoc", floatSetter(site.SetBufferSoc)},
- {"/bufferStartSoc", floatSetter(site.SetBufferStartSoc)},
- {"/batteryDischargeControl", boolSetter(site.SetBatteryDischargeControl)},
- {"/prioritySoc", floatSetter(site.SetPrioritySoc)},
- {"/residualPower", floatSetter(site.SetResidualPower)},
- {"/smartCostLimit", floatPtrSetter(pass(func(limit *float64) {
+ {"bufferSoc", floatSetter(site.SetBufferSoc)},
+ {"bufferStartSoc", floatSetter(site.SetBufferStartSoc)},
+ {"batteryDischargeControl", boolSetter(site.SetBatteryDischargeControl)},
+ {"prioritySoc", floatSetter(site.SetPrioritySoc)},
+ {"residualPower", floatSetter(site.SetResidualPower)},
+ {"smartCostLimit", floatPtrSetter(pass(func(limit *float64) {
for _, lp := range site.Loadpoints() {
lp.SetSmartCostLimit(limit)
}
}))},
- {"/batteryGridChargeLimit", floatPtrSetter(pass(site.SetBatteryGridChargeLimit))},
+ {"batteryGridChargeLimit", floatPtrSetter(pass(site.SetBatteryGridChargeLimit))},
} {
- if err := m.Handler.ListenSetter(topic+s.topic, s.fun); err != nil {
+ if err := m.Handler.ListenSetter(topic+"/"+s.topic, s.fun); err != nil {
return err
}
}
@@ -195,19 +195,19 @@ func (m *MQTT) listenSiteSetters(topic string, site site.API) error {
func (m *MQTT) listenLoadpointSetters(topic string, site site.API, lp loadpoint.API) error {
for _, s := range []setter{
- {"/mode", setterFunc(api.ChargeModeString, pass(lp.SetMode))},
- {"/phases", intSetter(lp.SetPhases)},
- {"/limitSoc", intSetter(pass(lp.SetLimitSoc))},
- {"/minCurrent", floatSetter(lp.SetMinCurrent)},
- {"/maxCurrent", floatSetter(lp.SetMaxCurrent)},
- {"/limitEnergy", floatSetter(pass(lp.SetLimitEnergy))},
- {"/enableThreshold", floatSetter(pass(lp.SetEnableThreshold))},
- {"/disableThreshold", floatSetter(pass(lp.SetDisableThreshold))},
- {"/enableDelay", durationSetter(pass(lp.SetEnableDelay))},
- {"/disableDelay", durationSetter(pass(lp.SetDisableDelay))},
- {"/smartCostLimit", floatPtrSetter(pass(lp.SetSmartCostLimit))},
- {"/batteryBoost", boolSetter(lp.SetBatteryBoost)},
- {"/planEnergy", func(payload string) error {
+ {"mode", setterFunc(api.ChargeModeString, pass(lp.SetMode))},
+ {"phases", intSetter(lp.SetPhases)},
+ {"limitSoc", intSetter(pass(lp.SetLimitSoc))},
+ {"minCurrent", floatSetter(lp.SetMinCurrent)},
+ {"maxCurrent", floatSetter(lp.SetMaxCurrent)},
+ {"limitEnergy", floatSetter(pass(lp.SetLimitEnergy))},
+ {"enableThreshold", floatSetter(pass(lp.SetEnableThreshold))},
+ {"disableThreshold", floatSetter(pass(lp.SetDisableThreshold))},
+ {"enableDelay", durationSetter(pass(lp.SetEnableDelay))},
+ {"disableDelay", durationSetter(pass(lp.SetDisableDelay))},
+ {"smartCostLimit", floatPtrSetter(pass(lp.SetSmartCostLimit))},
+ {"batteryBoost", boolSetter(lp.SetBatteryBoost)},
+ {"planEnergy", func(payload string) error {
var plan struct {
Time time.Time `json:"time"`
Value float64 `json:"value"`
@@ -218,9 +218,9 @@ func (m *MQTT) listenLoadpointSetters(topic string, site site.API, lp loadpoint.
}
return err
}},
- {"/vehicle", func(payload string) error {
+ {"vehicle", func(payload string) error {
// https://github.com/evcc-io/evcc/issues/11184 empty payload is swallowed by listener
- if payload == "-" {
+ if isEmpty(payload) {
lp.SetVehicle(nil)
return nil
}
@@ -231,7 +231,7 @@ func (m *MQTT) listenLoadpointSetters(topic string, site site.API, lp loadpoint.
return err
}},
} {
- if err := m.Handler.ListenSetter(topic+s.topic, s.fun); err != nil {
+ if err := m.Handler.ListenSetter(topic+"/"+s.topic, s.fun); err != nil {
return err
}
}
@@ -241,9 +241,9 @@ func (m *MQTT) listenLoadpointSetters(topic string, site site.API, lp loadpoint.
func (m *MQTT) listenVehicleSetters(topic string, v vehicle.API) error {
for _, s := range []setter{
- {topic + "/limitSoc", intSetter(pass(v.SetLimitSoc))},
- {topic + "/minSoc", intSetter(pass(v.SetMinSoc))},
- {topic + "/planSoc", func(payload string) error {
+ {"limitSoc", intSetter(pass(v.SetLimitSoc))},
+ {"minSoc", intSetter(pass(v.SetMinSoc))},
+ {"planSoc", func(payload string) error {
var plan struct {
Time time.Time `json:"time"`
Value int `json:"value"`
@@ -255,7 +255,7 @@ func (m *MQTT) listenVehicleSetters(topic string, v vehicle.API) error {
return err
}},
} {
- if err := m.Handler.ListenSetter(s.topic, s.fun); err != nil {
+ if err := m.Handler.ListenSetter(topic+"/"+s.topic, s.fun); err != nil {
return err
}
}
diff --git a/server/mqtt_setter.go b/server/mqtt_setter.go
index ee6214d21d..8541260b6f 100644
--- a/server/mqtt_setter.go
+++ b/server/mqtt_setter.go
@@ -1,6 +1,7 @@
package server
import (
+ "slices"
"strconv"
"time"
@@ -13,6 +14,10 @@ type setter struct {
fun func(string) error
}
+func isEmpty(payload string) bool {
+ return slices.Contains([]string{"-", "nil", "null", "none"}, payload)
+}
+
func setterFunc[T any](conv func(string) (T, error), set func(T) error) func(string) error {
return func(payload string) error {
val, err := conv(payload)
@@ -23,20 +28,26 @@ func setterFunc[T any](conv func(string) (T, error), set func(T) error) func(str
}
}
+// ptrSetter updates pointer api
+func ptrSetter[T any](conv func(string) (T, error), set func(*T) error) func(string) error {
+ return func(payload string) error {
+ var val *T
+ v, err := conv(payload)
+ if err == nil {
+ val = &v
+ } else if !isEmpty(payload) {
+ return err
+ }
+ return set(val)
+ }
+}
+
func floatSetter(set func(float64) error) func(string) error {
return setterFunc(parseFloat, set)
}
func floatPtrSetter(set func(*float64) error) func(string) error {
- return func(s string) error {
- var val *float64
- if f, err := parseFloat(s); err == nil {
- val = &f
- } else if s != "" {
- return err
- }
- return set(val)
- }
+ return ptrSetter(parseFloat, set)
}
func intSetter(set func(int) error) func(string) error {
diff --git a/tariff/edf-tempo.go b/tariff/edf-tempo.go
index 2adf03c2ec..2c41fee262 100644
--- a/tariff/edf-tempo.go
+++ b/tariff/edf-tempo.go
@@ -50,7 +50,7 @@ func NewEdfTempoFromConfig(other map[string]interface{}) (api.Tariff, error) {
}
if cc.ClientID == "" || cc.ClientSecret == "" {
- return nil, errors.New("missing credentials")
+ return nil, api.ErrMissingCredentials
}
if err := cc.init(); err != nil {
diff --git a/tariff/embed.go b/tariff/embed.go
index 9da6d63c8a..392f624b75 100644
--- a/tariff/embed.go
+++ b/tariff/embed.go
@@ -34,6 +34,14 @@ func (t *embed) init() error {
return err
}
+ if _, err := vm.Eval(fmt.Sprintf("charges = %f", t.Charges)); err != nil {
+ return err
+ }
+
+ if _, err := vm.Eval(fmt.Sprintf("tax = %f", t.Tax)); err != nil {
+ return err
+ }
+
prg, err := vm.Compile(t.Formula)
if err != nil {
return err
diff --git a/tariff/pun.go b/tariff/pun.go
index 29703bcf68..35edd0e55d 100644
--- a/tariff/pun.go
+++ b/tariff/pun.go
@@ -4,7 +4,6 @@ import (
"encoding/xml"
"fmt"
"io"
- "net/http"
"net/http/cookiejar"
"net/url"
"slices"
@@ -16,6 +15,7 @@ import (
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
+ "github.com/evcc-io/evcc/util/request"
"golang.org/x/net/html"
)
@@ -124,28 +124,21 @@ func (t *Pun) Type() api.TariffType {
}
func (t *Pun) getData(day time.Time) (api.Rates, error) {
- // Initial URL
- urlString := "https://www.mercatoelettrico.org/It/WebServerDataStore/MGP_Prezzi/" + day.Format("20060102") + "MGPPrezzi.xml"
-
// Cookie Jar zur Speicherung von Cookies zwischen den Requests
- jar, _ := cookiejar.New(nil)
- client := &http.Client{
- Jar: jar,
- }
+ client := request.NewClient(t.log)
+ client.Jar, _ = cookiejar.New(nil)
// Erster Request
- resp, err := client.Get(urlString)
+ uri := "https://storico.mercatoelettrico.org/It/WebServerDataStore/MGP_Prezzi/" + day.Format("20060102") + "MGPPrezzi.xml"
+ resp, err := client.Get(uri)
if err != nil {
- fmt.Println("Error fetching URL:", err)
return nil, err
}
defer resp.Body.Close()
- body, _ := io.ReadAll(resp.Body)
- formData, err := parseFormFields(body)
+ formData, err := parseFormFields(resp.Body)
if err != nil {
- fmt.Println("Error parsing form fields:", err)
- return nil, err
+ return nil, fmt.Errorf("form fields: %w", err)
}
redirectURL := resp.Request.URL.String()
@@ -164,21 +157,16 @@ func (t *Pun) getData(day time.Time) (api.Rates, error) {
defer resp.Body.Close()
// Erneuter Request auf die ursprüngliche URL
- resp, err = client.Get(urlString)
+ resp, err = client.Get(uri)
if err != nil {
- fmt.Println("Error fetching URL after form submission:", err)
return nil, err
}
defer resp.Body.Close()
// Verarbeitung der erhaltenen Daten
- body, _ = io.ReadAll(resp.Body)
- xmlData := []byte(string(body)) // Ersetzen Sie [Ihr XML-Datenstring hier] mit Ihrem XML-String
-
var dataSet NewDataSet
- err = xml.Unmarshal(xmlData, &dataSet)
- if err != nil {
- fmt.Println("Error unmarshalling XML: ", err)
+ if err := xml.NewDecoder(resp.Body).Decode(&dataSet); err != nil {
+ return nil, err
}
data := make(api.Rates, 0, len(dataSet.Prezzi))
@@ -186,17 +174,17 @@ func (t *Pun) getData(day time.Time) (api.Rates, error) {
for _, p := range dataSet.Prezzi {
date, err := time.Parse("20060102", p.Data)
if err != nil {
- fmt.Println("Error parsing date: ", err)
+ return nil, fmt.Errorf("parse date: %w", err)
}
hour, err := strconv.Atoi(p.Ora)
if err != nil {
- fmt.Println("Error parsing hour: ", err)
+ return nil, fmt.Errorf("parse hour: %w", err)
}
location, err := time.LoadLocation("Europe/Rome")
if err != nil {
- fmt.Println("Error loading location: ", err)
+ return nil, fmt.Errorf("load location: %w", err)
}
start := time.Date(date.Year(), date.Month(), date.Day(), hour-1, 0, 0, 0, location)
@@ -205,7 +193,7 @@ func (t *Pun) getData(day time.Time) (api.Rates, error) {
priceStr := strings.Replace(p.PUN, ",", ".", -1) // Ersetzen Sie Komma durch Punkt
price, err := strconv.ParseFloat(priceStr, 64)
if err != nil {
- fmt.Println("Error parsing price: ", err)
+ return nil, fmt.Errorf("parse price: %w", err)
}
ar := api.Rate{
@@ -220,18 +208,16 @@ func (t *Pun) getData(day time.Time) (api.Rates, error) {
return data, nil
}
-func parseFormFields(body []byte) (url.Values, error) {
- formData := url.Values{}
- doc, err := html.Parse(strings.NewReader(string(body)))
+func parseFormFields(body io.Reader) (url.Values, error) {
+ data := url.Values{}
+ doc, err := html.Parse(body)
if err != nil {
- return formData, err
+ return data, err
}
var f func(*html.Node)
f = func(n *html.Node) {
if n.Type == html.ElementNode && n.Data == "input" {
- inputType := ""
- inputName := ""
- inputValue := ""
+ var inputType, inputName, inputValue string
for _, a := range n.Attr {
if a.Key == "type" {
inputType = a.Val
@@ -242,7 +228,7 @@ func parseFormFields(body []byte) (url.Values, error) {
}
}
if inputType == "hidden" && inputName != "" {
- formData.Set(inputName, inputValue)
+ data.Set(inputName, inputValue)
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
@@ -250,5 +236,5 @@ func parseFormFields(body []byte) (url.Values, error) {
}
}
f(doc)
- return formData, nil
+ return data, nil
}
diff --git a/templates/definition/charger/keba-modbus.yaml b/templates/definition/charger/keba-modbus.yaml
index 11cfb4829b..dbaf2fddcf 100644
--- a/templates/definition/charger/keba-modbus.yaml
+++ b/templates/definition/charger/keba-modbus.yaml
@@ -24,6 +24,6 @@ params:
render: |
type: keba-modbus
{{- include "modbus" . }}
- {{- if .welcomecharge }}
+ {{- if eq .welcomecharge "true" }}
features: ["welcomecharge"]
{{- end }}
diff --git a/templates/definition/meter/enphase.yaml b/templates/definition/meter/enphase.yaml
index 12b497ad00..f49da8019f 100644
--- a/templates/definition/meter/enphase.yaml
+++ b/templates/definition/meter/enphase.yaml
@@ -74,6 +74,6 @@ render: |
password: {{ .token }}
insecure: true
{{- end }}
- jq: .[].devices | map(.percentFull) | add / length
+ jq: '[.[].devices[] | select(.percentFull != null) | .percentFull] | add / length'
capacity: {{ .capacity }} # kWh
{{- end }}
diff --git a/templates/definition/meter/homewizard-kwh.yaml b/templates/definition/meter/homewizard-kwh.yaml
new file mode 100644
index 0000000000..6f8b199419
--- /dev/null
+++ b/templates/definition/meter/homewizard-kwh.yaml
@@ -0,0 +1,13 @@
+template: homewizard-kwh
+products:
+ - brand: HomeWizard
+ description:
+ generic: kWh Meter
+params:
+ - name: usage
+ choice: ["pv"]
+ - name: host
+render: |
+ type: homewizard
+ uri: http://{{ .host }}
+ usage: {{ .usage }}
diff --git a/templates/definition/meter/homewizard.yaml b/templates/definition/meter/homewizard-p1.yaml
similarity index 81%
rename from templates/definition/meter/homewizard.yaml
rename to templates/definition/meter/homewizard-p1.yaml
index 6afc3bd167..4f2b70b50b 100644
--- a/templates/definition/meter/homewizard.yaml
+++ b/templates/definition/meter/homewizard-p1.yaml
@@ -5,8 +5,9 @@ products:
generic: Wi-Fi P1 Meter
params:
- name: usage
- choice: ["grid", "pv"]
+ choice: ["grid"]
- name: host
render: |
type: homewizard
uri: http://{{ .host }}
+ usage: {{ .usage }}
diff --git a/templates/definition/meter/huawei-emma.yaml b/templates/definition/meter/huawei-emma.yaml
new file mode 100644
index 0000000000..2172e4c1f7
--- /dev/null
+++ b/templates/definition/meter/huawei-emma.yaml
@@ -0,0 +1,108 @@
+template: huawei-emma
+products:
+ - brand: Huawei
+ description:
+ generic: EMMA
+params:
+ - name: usage
+ choice: ["grid", "pv", "battery"]
+ allinone: true
+ - name: modbus
+ choice: ["tcpip"]
+ - name: capacity
+ advanced: true
+render: |
+ type: custom
+ {{- if eq .usage "grid" }}
+ power:
+ source: modbus
+ id: 0
+ uri: {{ .host }}:{{ .port }}
+ register:
+ address: 31657 # Active power of built-in electric energy sensor
+ type: holding
+ decode: int32
+ energy:
+ source: modbus
+ id: 0
+ uri: {{ .host }}:{{ .port }}
+ register:
+ address: 31679 # Total negative active energy of built-in electric energy sensor
+ type: holding
+ decode: int64
+ scale: 0.01
+ currents:
+ - source: modbus
+ id: 0
+ uri: {{ .host }}:{{ .port }}
+ register:
+ address: 31651 # Huawei phase A grid current
+ type: holding
+ decode: int32
+ scale: 0.1
+ - source: modbus
+ id: 0
+ uri: {{ .host }}:{{ .port }}
+ register:
+ address: 31653 # Huawei phase B grid current
+ type: holding
+ decode: int32
+ scale: 0.1
+ - source: modbus
+ id: 0
+ uri: {{ .host }}:{{ .port }}
+ register:
+ address: 31655 # Huawei phase C grid current
+ type: holding
+ decode: int32
+ scale: 0.1
+ {{- end }}
+ {{- if eq .usage "pv" }}
+ power:
+ source: modbus
+ id: 0
+ uri: {{ .host }}:{{ .port }}
+ register:
+ address: 30354 # Active power
+ type: holding
+ decode: int32
+ energy:
+ source: modbus
+ id: 0
+ uri: {{ .host }}:{{ .port }}
+ register:
+ address: 30344 # E-Total
+ type: holding
+ decode: uint32
+ scale: 0.1
+ {{- end }}
+ {{- if eq .usage "battery" }}
+ power:
+ source: modbus
+ id: 0
+ uri: {{ .host }}:{{ .port }}
+ register:
+ address: 30360
+ type: holding
+ decode: int32nan
+ scale: -1
+ energy:
+ source: modbus
+ id: 0
+ uri: {{ .host }}:{{ .port }}
+ register:
+ address: 30312 # Total discharge
+ type: holding
+ decode: uint32nan
+ scale: 0.01
+ soc:
+ source: modbus
+ id: 0
+ uri: {{ .host }}:{{ .port }}
+ register:
+ address: 30368
+ type: holding
+ decode: uint16nan
+ scale: 0.01
+ capacity: {{ .capacity }} # kWh
+ {{- end }}
diff --git a/templates/definition/meter/qcells-hybrid-cloud b/templates/definition/meter/qcells-hybrid-cloud.yaml
similarity index 98%
rename from templates/definition/meter/qcells-hybrid-cloud
rename to templates/definition/meter/qcells-hybrid-cloud.yaml
index 1fce99390f..8b1f8bf0f6 100644
--- a/templates/definition/meter/qcells-hybrid-cloud
+++ b/templates/definition/meter/qcells-hybrid-cloud.yaml
@@ -1,6 +1,6 @@
template: qcells-hybrid-cloud
products:
- - brand: QCells (based on Solax)
+ - brand: QCells
description:
de: Hybrid-Wechselrichter (Cloud)
en: Hybrid-Inverter (Cloud)
diff --git a/templates/definition/meter/shelly-1pm.yaml b/templates/definition/meter/shelly-1pm.yaml
index 3dd14314d6..bb6383494f 100644
--- a/templates/definition/meter/shelly-1pm.yaml
+++ b/templates/definition/meter/shelly-1pm.yaml
@@ -2,11 +2,11 @@ template: shelly-1pm
products:
- brand: Shelly
description:
- generic: 1PM, EM, Plug S
+ generic: 1PM, EM, Plug S, Pro 3EM in monophase mode
group: switchsockets
params:
- name: usage
- choice: ["pv", "charge"]
+ choice: ["grid", "pv", "charge"]
- name: host
- name: user
- name: password
diff --git a/templates/definition/meter/sma-hybrid.yaml b/templates/definition/meter/sma-hybrid.yaml
index cb23c740a2..7431fbc7be 100644
--- a/templates/definition/meter/sma-hybrid.yaml
+++ b/templates/definition/meter/sma-hybrid.yaml
@@ -23,6 +23,13 @@ params:
type: duration
default: 30s
advanced: true
+ - name: chargepower
+ default: 100000 # 100kW, inverter will use it's maximum by default
+ advanced: true
+ help:
+ de: Ladeleistung für Netzladen in W
+ en: Charging power for grid charging in W
+ usages: ["battery"]
render: |
type: custom
{{- if eq .usage "grid" }}
@@ -167,7 +174,7 @@ render: |
source: sequence
set:
- source: const
- value: -2147483647 # Wirkleistungsvorgabe
+ value: -{{ .chargepower }} # Wirkleistungsvorgabe
set:
source: modbus
{{- include "modbus" . | indent 12 }}
diff --git a/templates/definition/meter/sofarsolar-g3.yaml b/templates/definition/meter/sofarsolar-g3.yaml
index 8084b2dd7d..a86f4d19f5 100644
--- a/templates/definition/meter/sofarsolar-g3.yaml
+++ b/templates/definition/meter/sofarsolar-g3.yaml
@@ -15,8 +15,8 @@ products:
# capabilities: ["battery-control"]
requirements:
description:
- de: Die Verbindung über einen LSE-3 Logger Stick mittels ModBus TCP ist möglich, jedoch kann Battery Control darüber nicht genutzt werden. Der LSW-3 WLAN Stick wird nicht unterstützt. Bei seriellem Anschluss via RS485 am COM Port ist eine wechselrichterseitige Terminierung notwendig.
- en: Connection via LSE-3 logger stick using ModBus TCP is possible, however does not allow Battery Control. The LSW-3 WiFi stick is not supported. For RS485 serial connection using the inverter's COM port the inverter's side must be properly terminated.
+ de: Die Verbindung über einen LSE-3 Logger Stick mittels LAN Anschluss und ModBus TCP über Port 8899 ist die einfachste Anbindung. Der LSW-3 WLAN Stick wird nicht unterstützt. Bei seriellem Anschluss via RS485 am COM Port ist eine wechselrichterseitige Terminierung notwendig.
+ en: LSE-3 logger stick using a LAN connection and ModBus TCP via the port 8899 is the easiest connection. The LSW-3 WiFi stick is not supported. For a RS485 serial connection using the inverter's COM port the inverter's side must be properly terminated.
capabilities: ["battery-control"]
params:
- name: usage
diff --git a/templates/definition/meter/zendure.yaml b/templates/definition/meter/zendure.yaml
new file mode 100644
index 0000000000..5f53f2dcfe
--- /dev/null
+++ b/templates/definition/meter/zendure.yaml
@@ -0,0 +1,34 @@
+template: zendure
+products:
+ - brand: Zendure
+ description:
+ generic: Hyper 2000
+requirements:
+ evcc: ["skiptest"]
+params:
+ - name: usage
+ choice: ["pv", "battery"]
+ - name: account
+ required: true
+ example: dev@zendure.com
+ - name: serial
+ required: true
+ example: VU5D99F74021B04
+ help:
+ de: "Zu finden in der Zendure App in den Einstellungen des Geräts"
+ en: "You can find this in the Zendure App in the settings of the device"
+ - name: region
+ type: choice
+ default: EU
+ validvalues: ["EU", "Global"]
+ - name: capacity
+ default: 2
+ advanced: true
+ - name: timeout
+render: |
+ type: zendure
+ usage: {{ .usage }}
+ region: {{ .region }}
+ account: {{ .account }}
+ serial: {{ .serial }}
+ timeout: {{ .timeout }}
diff --git a/templates/definition/tariff/energinet.yaml b/templates/definition/tariff/energinet.yaml
index c3e448ee28..3f45119e6d 100644
--- a/templates/definition/tariff/energinet.yaml
+++ b/templates/definition/tariff/energinet.yaml
@@ -5,6 +5,7 @@ requirements:
description:
de: "Nur für Dänemark verfügbar."
en: "Only available for Denmark."
+ evcc: ["skiptest"]
group: price
params:
- name: region
diff --git a/templates/definition/tariff/pun.yaml b/templates/definition/tariff/pun.yaml
index 91b4c826c7..5a5ec47d8d 100644
--- a/templates/definition/tariff/pun.yaml
+++ b/templates/definition/tariff/pun.yaml
@@ -2,6 +2,7 @@ template: pun
products:
- brand: PUN Orario
requirements:
+ evcc: ["skiptest"]
description:
de: "Preisdaten von https://www.mercatoelettrico.org/it/. Wird oft zur Einspeisung ins Netz verwendet. Nur für Italien verfügbar."
en: "Price data from https://www.mercatoelettrico.org/it/. Often used for feeding into the grid. Only available for Italy."
diff --git a/templates/definition/vehicle/bmw.yaml b/templates/definition/vehicle/bmw.yaml
index 0fb06944bc..1a1391ad0a 100644
--- a/templates/definition/vehicle/bmw.yaml
+++ b/templates/definition/vehicle/bmw.yaml
@@ -15,6 +15,8 @@ params:
# - EU
default: EU
advanced: true
+ - name: coarsecurrent
+ advanced: true
- name: welcomecharge
advanced: true
render: |
@@ -24,6 +26,12 @@ render: |
{{- if ne .region "EU" }}
region: {{ .region }}
{{- end }}
- {{- if .welcomecharge }}
- features: ["welcomecharge"]
+ {{- if or (eq .coarsecurrent "true") (eq .welcomecharge "true") }}
+ features:
+ {{- if eq .coarsecurrent "true" }}
+ - coarsecurrent
+ {{- end }}
+ {{- if eq .welcomecharge "true" }}
+ - welcomecharge
+ {{- end }}
{{- end }}
diff --git a/templates/definition/vehicle/dacia.yaml b/templates/definition/vehicle/dacia.yaml
index c48e746c25..a62f44f0c8 100644
--- a/templates/definition/vehicle/dacia.yaml
+++ b/templates/definition/vehicle/dacia.yaml
@@ -10,6 +10,6 @@ render: |
type: dacia
{{ include "vehicle-base" . }}
{{ include "vehicle-identify" . }}
- {{- if .welcomecharge }}
+ {{- if eq .welcomecharge "true" }}
features: ["welcomecharge"]
{{- end }}
diff --git a/templates/definition/vehicle/fiat.yaml b/templates/definition/vehicle/fiat.yaml
index d55272f7b7..71923bf9dc 100644
--- a/templates/definition/vehicle/fiat.yaml
+++ b/templates/definition/vehicle/fiat.yaml
@@ -18,6 +18,6 @@ render: |
pin: {{ .pin }} # mandatory to deep refresh Soc
{{- end }}
{{ include "vehicle-identify" . }}
- {{- if .welcomecharge }}
+ {{- if eq .welcomecharge "true" }}
features: ["welcomecharge"]
{{- end }}
diff --git a/templates/definition/vehicle/mini.yaml b/templates/definition/vehicle/mini.yaml
index 2e797d79a4..6b1b7a12f5 100644
--- a/templates/definition/vehicle/mini.yaml
+++ b/templates/definition/vehicle/mini.yaml
@@ -24,6 +24,6 @@ render: |
{{- if ne .region "EU" }}
region: {{ .region }}
{{- end }}
- {{- if .welcomecharge }}
+ {{- if eq .welcomecharge "true" }}
features: ["welcomecharge"]
{{- end }}
diff --git a/templates/definition/vehicle/polestar.yaml b/templates/definition/vehicle/polestar.yaml
index 1d1d2d5182..d5cda5bdac 100644
--- a/templates/definition/vehicle/polestar.yaml
+++ b/templates/definition/vehicle/polestar.yaml
@@ -1,6 +1,8 @@
template: polestar
products:
- brand: Polestar
+requirements:
+ evcc: ["skiptest"]
params:
- preset: vehicle-base
- preset: vehicle-identify
diff --git a/templates/definition/vehicle/seat-cupra.yaml b/templates/definition/vehicle/seat-cupra.yaml
index aacb774a8d..6aab57ac07 100644
--- a/templates/definition/vehicle/seat-cupra.yaml
+++ b/templates/definition/vehicle/seat-cupra.yaml
@@ -12,6 +12,6 @@ render: |
type: cupra
{{ include "vehicle-base" . }}
{{ include "vehicle-identify" . }}
- {{- if .welcomecharge }}
+ {{- if eq .welcomecharge "true" }}
features: ["welcomecharge"]
{{- end }}
diff --git a/templates/definition/vehicle/smart-hello.yaml b/templates/definition/vehicle/smart-hello.yaml
index 064990d5e9..61f799ac22 100644
--- a/templates/definition/vehicle/smart-hello.yaml
+++ b/templates/definition/vehicle/smart-hello.yaml
@@ -12,6 +12,6 @@ render: |
type: smart-hello
{{ include "vehicle-base" . }}
{{ include "vehicle-identify" . }}
- {{- if .welcomecharge }}
+ {{- if eq .welcomecharge "true" }}
features: ["welcomecharge"]
{{- end }}
diff --git a/templates/definition/vehicle/tessie.yaml b/templates/definition/vehicle/tessie.yaml
new file mode 100644
index 0000000000..2a6f3c2bc3
--- /dev/null
+++ b/templates/definition/vehicle/tessie.yaml
@@ -0,0 +1,100 @@
+template: tessie
+products:
+ - description:
+ generic: Tessie
+group: generic
+requirements:
+ description:
+ de: Verbinden Sie Ihr Tesla-Fahrzeug über die Tessie-API. Dies wird das Fahrzeug niemals aufwecken; das Polling kann auf „always“ und interval „1M“ eingestellt werden. Wenn das Fahrzeug wach ist, sind die Daten normalerweise weniger als 15 Sekunden alt. Wenn das Fahrzeug schläft, stammen die Daten aus dem Zeitpunkt, zu dem es eingeschlafen ist. Holen Sie sich Ihr Token unter https://dash.tessie.com/settings/api
+ en: Connect your Tesla using the Tessie API. This will never wake up the car, polling can be set to "always" and interval "1M". If the vehicle is awake, the data is usually less than 15 seconds old. If the vehicle is asleep, the data is from the time the vehicle went to sleep. Get your token at https://dash.tessie.com/settings/api
+params:
+ - name: title
+ - name: vin
+ description:
+ de: Fahrzeug-VIN
+ en: Vehicle VIN
+ required: true
+ - name: token
+ description:
+ de: Tessie API Token
+ en: Tessie API Token
+ required: true
+ - name: capacity
+ - name: phases
+ advanced: true
+ - name: icon
+ default: car
+ advanced: true
+ - preset: vehicle-identify
+render: |
+ type: custom
+ {{- include "vehicle-common" . }}
+ {{- include "vehicle-identify" . }}
+ soc: # battery state of charge (%)
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/state?use_cache=true
+ headers:
+ Authorization: Bearer {{ .token }}
+ jq: .charge_state.usable_battery_level
+ status:
+ source: combined
+ plugged:
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/state?use_cache=true
+ headers:
+ Authorization: Bearer {{ .token }}
+ jq: .charge_state.charge_port_door_open
+ charging:
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/state?use_cache=true
+ headers:
+ Authorization: Bearer {{ .token }}
+ jq: .charge_state.charging_state == "Charging"
+ range:
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/state?use_cache=true
+ headers:
+ Authorization: Bearer {{ .token }}
+ jq: .charge_state.battery_range * 1.60934
+ odometer:
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/state?use_cache=true
+ headers:
+ Authorization: Bearer {{ .token }}
+ jq: .vehicle_state.odometer * 1.60934
+ climater:
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/state?use_cache=true
+ headers:
+ Authorization: Bearer {{ .token }}
+ jq: .climate_state.is_climate_on
+ limitsoc:
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/state?use_cache=true
+ headers:
+ Authorization: Bearer {{ .token }}
+ jq: .charge_state.charge_limit_soc
+ getMaxCurrent:
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/state?use_cache=true
+ headers:
+ Authorization: Bearer {{ .token }}
+ jq: .charge_state.charge_current_request
+ chargeEnable:
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/command/start_charging?retry_duration=40&wait_for_completion=true
+ headers:
+ Authorization: Bearer {{ .token }}
+ method: POST
+ maxcurrent:
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/command/set_charging_amps?retry_duration=40&wait_for_completion=true&s=${maxcurrent}
+ headers:
+ Authorization: Bearer {{ .token }}
+ method: POST
+ wakeup:
+ source: http
+ uri: https://api.tessie.com/{{ .vin }}/wake
+ headers:
+ Authorization: Bearer {{ .token }}
+ method: POST
diff --git a/tests/auth.spec.js b/tests/auth.spec.js
index 783b337b1f..4c7078ec69 100644
--- a/tests/auth.spec.js
+++ b/tests/auth.spec.js
@@ -6,7 +6,7 @@ test.use({ baseURL: baseUrl() });
const BASIC = "basics.evcc.yaml";
test("set initial password", async ({ page }) => {
- await start(BASIC);
+ await start(BASIC, null, "");
await page.goto("/");
const modal = page.getByTestId("password-modal");
@@ -34,7 +34,7 @@ test("set initial password", async ({ page }) => {
});
test("login", async ({ page }) => {
- await start(BASIC, "password.sql");
+ await start(BASIC, "password.sql", "");
await page.goto("/");
// go to config
@@ -61,7 +61,7 @@ test("login", async ({ page }) => {
});
test("http iframe hint", async ({ page }) => {
- await start(BASIC, "password.sql");
+ await start(BASIC, "password.sql", "");
await page.goto("/");
// go to config
@@ -89,7 +89,7 @@ test("http iframe hint", async ({ page }) => {
});
test("update password", async ({ page }) => {
- const instance = await start(BASIC, "password.sql");
+ await start(BASIC, "password.sql", "");
await page.goto("/");
const oldPassword = "secret";
@@ -140,3 +140,19 @@ test("update password", async ({ page }) => {
await stop();
});
+
+test("disable auth", async ({ page }) => {
+ await start(BASIC, null, "--disable-auth");
+ await page.goto("/");
+
+ // no password modal
+ const modal = page.getByTestId("password-modal");
+ await expect(modal).not.toBeVisible();
+
+ // configuration page without login
+ await page.getByTestId("topnavigation-button").click();
+ await page.getByRole("link", { name: "Configuration" }).click();
+ await expect(page.getByRole("heading", { name: "Configuration" })).toBeVisible();
+
+ await stop();
+});
diff --git a/tests/basics.spec.js b/tests/basics.spec.js
index 4a85041430..a832d95626 100644
--- a/tests/basics.spec.js
+++ b/tests/basics.spec.js
@@ -4,7 +4,7 @@ import { start, stop, baseUrl } from "./evcc";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
- await start("basics.evcc.yaml", "password.sql");
+ await start("basics.evcc.yaml");
});
test.afterAll(async () => {
await stop();
diff --git a/tests/battery-settings.spec.js b/tests/battery-settings.spec.js
index 42fe67c253..4dba33b1ae 100644
--- a/tests/battery-settings.spec.js
+++ b/tests/battery-settings.spec.js
@@ -4,7 +4,7 @@ import { enableExperimental } from "./utils";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
- await start("battery-settings.evcc.yaml", "password.sql");
+ await start("battery-settings.evcc.yaml");
});
test.afterAll(async () => {
await stop();
diff --git a/tests/boot.spec.js b/tests/boot.spec.js
index 0b2e983a02..4df26c1246 100644
--- a/tests/boot.spec.js
+++ b/tests/boot.spec.js
@@ -4,7 +4,7 @@ import { enableExperimental } from "./utils";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
- await start("battery-settings.evcc.yaml", "password.sql");
+ await start("battery-settings.evcc.yaml");
});
test.afterAll(async () => {
await stop();
diff --git a/tests/config-battery.spec.js b/tests/config-battery.spec.js
index 2a86c64170..9cb90ee6f4 100644
--- a/tests/config-battery.spec.js
+++ b/tests/config-battery.spec.js
@@ -1,7 +1,7 @@
import { test, expect } from "@playwright/test";
import { start, stop, restart, baseUrl } from "./evcc";
import { startSimulator, stopSimulator, simulatorUrl, simulatorHost } from "./simulator";
-import { enableExperimental, login } from "./utils";
+import { enableExperimental } from "./utils";
const CONFIG_GRID_ONLY = "config-grid-only.evcc.yaml";
@@ -9,7 +9,7 @@ test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
await startSimulator();
- await start(CONFIG_GRID_ONLY, "password.sql");
+ await start(CONFIG_GRID_ONLY);
});
test.afterAll(async () => {
await stop();
@@ -25,7 +25,6 @@ test.describe("battery meter", async () => {
await page.getByRole("button", { name: "Apply changes" }).click();
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
await expect(page.getByTestId("battery")).toHaveCount(0);
@@ -73,7 +72,6 @@ test.describe("battery meter", async () => {
test("advanced fields", async ({ page }) => {
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
await page.getByRole("button", { name: "Add solar or battery" }).click();
diff --git a/tests/config-grid.spec.js b/tests/config-grid.spec.js
index dac01b1a02..d10fc352b3 100644
--- a/tests/config-grid.spec.js
+++ b/tests/config-grid.spec.js
@@ -1,7 +1,7 @@
import { test, expect } from "@playwright/test";
import { start, stop, restart, baseUrl } from "./evcc";
import { startSimulator, stopSimulator, simulatorUrl, simulatorHost } from "./simulator";
-import { enableExperimental, login } from "./utils";
+import { enableExperimental } from "./utils";
const CONFIG_EMPTY = "config-empty.evcc.yaml";
@@ -9,7 +9,7 @@ test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
await startSimulator();
- await start(CONFIG_EMPTY, "password.sql");
+ await start(CONFIG_EMPTY);
});
test.afterAll(async () => {
await stop();
@@ -32,7 +32,6 @@ test.describe("grid meter", async () => {
await page.getByRole("button", { name: "Apply changes" }).click();
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
await expect(page.getByTestId("grid")).toHaveCount(1);
diff --git a/tests/config-messaging.spec.js b/tests/config-messaging.spec.js
index fa7fe88a0f..c0b55f5b65 100644
--- a/tests/config-messaging.spec.js
+++ b/tests/config-messaging.spec.js
@@ -1,6 +1,6 @@
import { test, expect } from "@playwright/test";
import { start, stop, baseUrl } from "./evcc";
-import { enableExperimental, login } from "./utils";
+import { enableExperimental } from "./utils";
const CONFIG_GRID_ONLY = "config-grid-only.evcc.yaml";
@@ -14,13 +14,12 @@ const SELECT_ALL = "ControlOrMeta+KeyA";
async function goToConfig(page) {
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
}
test.describe("messaging", async () => {
test("save a comment", async ({ page }) => {
- await start(CONFIG_GRID_ONLY, "password.sql");
+ await start(CONFIG_GRID_ONLY);
await goToConfig(page);
await page.getByTestId("messaging").getByRole("button", { name: "edit" }).click();
diff --git a/tests/config-mqtt.spec.js b/tests/config-mqtt.spec.js
index d2dacaba63..abec62f540 100644
--- a/tests/config-mqtt.spec.js
+++ b/tests/config-mqtt.spec.js
@@ -1,6 +1,6 @@
import { test, expect } from "@playwright/test";
import { start, stop, restart, baseUrl } from "./evcc";
-import { enableExperimental, login } from "./utils";
+import { enableExperimental } from "./utils";
const CONFIG = "config-grid-only.evcc.yaml";
@@ -8,9 +8,8 @@ test.use({ baseURL: baseUrl() });
test.describe.configure({ mode: "parallel" });
test.beforeEach(async ({ page }) => {
- await start(CONFIG, "password.sql");
+ await start(CONFIG);
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
});
diff --git a/tests/config-tariffs.spec.js b/tests/config-tariffs.spec.js
index fa554a04ae..9d900497b2 100644
--- a/tests/config-tariffs.spec.js
+++ b/tests/config-tariffs.spec.js
@@ -1,6 +1,7 @@
import { test, expect } from "@playwright/test";
import { start, stop, restart, baseUrl } from "./evcc";
-import { enableExperimental, login } from "./utils";
+import { enableExperimental } from "./utils";
+
const CONFIG_GRID_ONLY = "config-grid-only.evcc.yaml";
const CONFIG_WITH_TARIFFS = "config-with-tariffs.evcc.yaml";
@@ -15,13 +16,12 @@ const SELECT_ALL = "ControlOrMeta+KeyA";
async function goToConfig(page) {
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
}
test.describe("tariffs", async () => {
test("tariffs not configured", async ({ page }) => {
- await start(CONFIG_GRID_ONLY, "password.sql");
+ await start(CONFIG_GRID_ONLY);
await goToConfig(page);
await expect(page.getByTestId("tariffs")).toBeVisible();
@@ -31,7 +31,7 @@ test.describe("tariffs", async () => {
});
test("tariffs via ui", async ({ page }) => {
- await start(CONFIG_GRID_ONLY, "password.sql");
+ await start(CONFIG_GRID_ONLY);
await goToConfig(page);
await page.getByTestId("tariffs").getByRole("button", { name: "edit" }).click();
@@ -89,7 +89,7 @@ test.describe("tariffs", async () => {
});
test("tariffs from evcc.yaml", async ({ page }) => {
- await start(CONFIG_WITH_TARIFFS, "password.sql");
+ await start(CONFIG_WITH_TARIFFS);
await goToConfig(page);
await expect(page.getByTestId("tariffs")).toBeVisible();
diff --git a/tests/config-vehicles.spec.js b/tests/config-vehicles.spec.js
index d153f777f0..ada5c4ee43 100644
--- a/tests/config-vehicles.spec.js
+++ b/tests/config-vehicles.spec.js
@@ -1,6 +1,6 @@
import { test, expect } from "@playwright/test";
import { start, stop, restart, baseUrl } from "./evcc";
-import { enableExperimental, login } from "./utils";
+import { enableExperimental } from "./utils";
const CONFIG_GRID_ONLY = "config-grid-only.evcc.yaml";
const CONFIG_WITH_VEHICLE = "config-with-vehicle.evcc.yaml";
@@ -14,10 +14,9 @@ test.afterEach(async () => {
test.describe("vehicles", async () => {
test("create, edit and delete vehicles", async ({ page }) => {
- await start(CONFIG_GRID_ONLY, "password.sql");
+ await start(CONFIG_GRID_ONLY);
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
await expect(page.getByTestId("vehicle")).toHaveCount(0);
@@ -65,10 +64,9 @@ test.describe("vehicles", async () => {
});
test("config should survive restart", async ({ page }) => {
- await start(CONFIG_GRID_ONLY, "password.sql");
+ await start(CONFIG_GRID_ONLY);
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
await expect(page.getByTestId("vehicle")).toHaveCount(0);
@@ -99,10 +97,9 @@ test.describe("vehicles", async () => {
});
test("mixed config (yaml + db)", async ({ page }) => {
- await start(CONFIG_WITH_VEHICLE, "password.sql");
+ await start(CONFIG_WITH_VEHICLE);
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
await expect(page.getByTestId("vehicle")).toHaveCount(1);
@@ -120,10 +117,9 @@ test.describe("vehicles", async () => {
});
test("advanced fields", async ({ page }) => {
- await start(CONFIG_GRID_ONLY, "password.sql");
+ await start(CONFIG_GRID_ONLY);
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
await page.getByTestId("add-vehicle").click();
@@ -159,10 +155,9 @@ test.describe("vehicles", async () => {
});
test("save and restore rfid identifiers", async ({ page }) => {
- await start(CONFIG_GRID_ONLY, "password.sql");
+ await start(CONFIG_GRID_ONLY);
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
await page.getByTestId("add-vehicle").click();
diff --git a/tests/config.spec.js b/tests/config.spec.js
index 08329a84eb..229c81edec 100644
--- a/tests/config.spec.js
+++ b/tests/config.spec.js
@@ -1,13 +1,13 @@
import { test, expect } from "@playwright/test";
import { start, stop, baseUrl } from "./evcc";
-import { enableExperimental, login } from "./utils";
+import { enableExperimental } from "./utils";
const CONFIG_GRID_ONLY = "config-grid-only.evcc.yaml";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
- await start(CONFIG_GRID_ONLY, "password.sql");
+ await start(CONFIG_GRID_ONLY);
});
test.afterAll(async () => {
await stop();
@@ -18,12 +18,10 @@ test.describe("basics", async () => {
await page.goto("/");
await page.getByTestId("topnavigation-button").click();
await page.getByRole("link", { name: "Configuration" }).click();
- await login(page);
await expect(page.getByRole("heading", { name: "Configuration" })).toBeVisible();
});
test.skip("alert box should always be visible", async ({ page }) => {
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
await expect(page.getByRole("alert")).toBeVisible();
});
@@ -37,7 +35,6 @@ test.describe("general", async () => {
// change value in config
await page.goto("/#/config");
- await login(page);
await enableExperimental(page);
await expect(page.getByTestId("generalconfig-title")).toContainText("Hello World");
diff --git a/tests/evcc.js b/tests/evcc.js
index a7d2c278ff..a9d4bf1cf2 100644
--- a/tests/evcc.js
+++ b/tests/evcc.js
@@ -42,12 +42,12 @@ function dbPath() {
return path.join(os.tmpdir(), file);
}
-export async function start(config, sqlDumps) {
+export async function start(config, sqlDumps, flags = "--disable-auth") {
await _clean();
if (sqlDumps) {
await _restoreDatabase(sqlDumps);
}
- return await _start(config);
+ return await _start(config, flags);
}
export async function stop(instance) {
@@ -55,9 +55,9 @@ export async function stop(instance) {
await _clean();
}
-export async function restart(config) {
+export async function restart(config, flags = "--disable-auth") {
await _stop();
- await _start(config);
+ await _start(config, flags);
}
export async function cleanRestart(config, sqlDumps) {
@@ -77,14 +77,15 @@ async function _restoreDatabase(sqlDumps) {
}
}
-async function _start(config) {
+async function _start(config, flags = []) {
const configFile = config.includes("/") ? config : `tests/${config}`;
const port = workerPort();
log(`wait until port ${port} is available`);
// wait for port to be available
await waitOn({ resources: [`tcp:${port}`], reverse: true, log: true });
- log("starting evcc", { config, port });
- const instance = spawn(BINARY, ["--config", configFile], {
+ const additionalFlags = typeof flags === "string" ? [flags] : flags;
+ log("starting evcc", { config, port, additionalFlags });
+ const instance = spawn(BINARY, ["--config", configFile, additionalFlags], {
env: { EVCC_NETWORK_PORT: port.toString(), EVCC_DATABASE_DSN: dbPath() },
stdio: ["pipe", "pipe", "pipe"],
});
@@ -108,10 +109,17 @@ async function _stop(instance) {
log("evcc is down", { port });
return;
}
+ // check if auth is required
+ const res = await axios.get(`${baseUrl()}/api/auth/status`);
+ log("auth status", res.status, res.statusText, res.data);
+ let cookie;
+ // login required
+ if (!res.data) {
+ const res = await axios.post(`${baseUrl()}/api/auth/login`, { password: "secret" });
+ log("login", res.status, res.statusText);
+ cookie = res.headers["set-cookie"];
+ }
log("shutting down evcc", { port });
- const res = await axios.post(`${baseUrl()}/api/auth/login`, { password: "secret" });
- log(res.status, res.statusText);
- const cookie = res.headers["set-cookie"];
await axios.post(`${baseUrl()}/api/system/shutdown`, {}, { headers: { cookie } });
log(`wait until port ${port} is closed`);
await waitOn({ resources: [`tcp:${port}`], reverse: true, log: true });
diff --git a/tests/heating.spec.js b/tests/heating.spec.js
index d11df1968e..468b23d2e6 100644
--- a/tests/heating.spec.js
+++ b/tests/heating.spec.js
@@ -4,7 +4,7 @@ import { start, stop, baseUrl } from "./evcc";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
- await start("heating.evcc.yaml", "password.sql");
+ await start("heating.evcc.yaml");
});
test.afterAll(async () => {
await stop();
diff --git a/tests/limits.spec.js b/tests/limits.spec.js
index 2aa058955e..6ddd512007 100644
--- a/tests/limits.spec.js
+++ b/tests/limits.spec.js
@@ -12,7 +12,7 @@ test.afterAll(async () => {
});
test.beforeEach(async ({ page }) => {
- await start(simulatorConfig(), "password.sql");
+ await start(simulatorConfig());
await page.goto(simulatorUrl());
await page.getByLabel("Grid Power").fill("500");
diff --git a/tests/logs.spec.js b/tests/logs.spec.js
index 3d21c0f6b2..b92af0cc6f 100644
--- a/tests/logs.spec.js
+++ b/tests/logs.spec.js
@@ -1,11 +1,10 @@
import { test, expect } from "@playwright/test";
import { start, stop, baseUrl } from "./evcc";
-import { login } from "./utils";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
- await start("basics.evcc.yaml", "password.sql");
+ await start("basics.evcc.yaml");
});
test.afterAll(async () => {
await stop();
@@ -16,7 +15,6 @@ test.describe("opening logs", async () => {
await page.goto("/");
await page.getByTestId("topnavigation-button").click();
await page.getByRole("link", { name: "Configuration" }).click();
- await login(page);
await page.getByRole("link", { name: "Logs" }).click();
await expect(page.getByRole("heading", { name: "Logs", exact: false })).toBeVisible();
});
@@ -25,7 +23,6 @@ test.describe("opening logs", async () => {
await page.evaluate(() => window.app.raise({ message: "Fake Error" }));
await page.getByTestId("notification-icon").click();
await page.getByRole("link", { name: "View full logs" }).click();
- await login(page);
await expect(page.getByRole("heading", { name: "Logs", exact: false })).toBeVisible();
});
test("via need help", async ({ page }) => {
@@ -33,7 +30,6 @@ test.describe("opening logs", async () => {
await page.getByTestId("topnavigation-button").click();
await page.getByRole("button", { name: "Need Help?" }).click();
await page.getByRole("link", { name: "View logs" }).click();
- await login(page);
await expect(page.getByRole("heading", { name: "Logs", exact: false })).toBeVisible();
});
});
@@ -41,7 +37,6 @@ test.describe("opening logs", async () => {
test.describe("features", async () => {
test("content", async ({ page }) => {
await page.goto("/#/log");
- await login(page);
await page.getByTestId("log-search").fill("listening at");
await expect(page.getByTestId("log-content")).toContainText("listening at");
});
diff --git a/tests/modals.spec.js b/tests/modals.spec.js
index 7a7049e6e2..560a77e0e9 100644
--- a/tests/modals.spec.js
+++ b/tests/modals.spec.js
@@ -1,7 +1,6 @@
import { test, expect } from "@playwright/test";
import { start, stop, baseUrl } from "./evcc";
import { startSimulator, stopSimulator, simulatorConfig } from "./simulator";
-import { login } from "./utils";
const BASICS_CONFIG = "basics.evcc.yaml";
@@ -11,7 +10,7 @@ test.use({ baseURL: baseUrl() });
test.describe("Basics", async () => {
test.beforeAll(async () => {
- await start(BASICS_CONFIG, "password.sql");
+ await start(BASICS_CONFIG);
});
test.afterAll(async () => {
@@ -21,9 +20,6 @@ test.describe("Basics", async () => {
test("Menu options. No battery and grid.", async ({ page }) => {
for (const route of UI_ROUTES) {
await page.goto(route);
- if (route === "/#/config") {
- await login(page);
- }
await page.getByTestId("topnavigation-button").click();
await expect(page.getByRole("button", { name: "User Interface" })).toBeVisible();
@@ -53,7 +49,7 @@ test.describe("Basics", async () => {
test.describe("Advanced", async () => {
test.beforeAll(async () => {
await startSimulator();
- await start(simulatorConfig(), "password.sql");
+ await start(simulatorConfig());
});
test.afterAll(async () => {
@@ -64,9 +60,6 @@ test.describe("Advanced", async () => {
test("Menu options. All available.", async ({ page }) => {
for (const route of UI_ROUTES) {
await page.goto(route);
- if (route === "/#/config") {
- await login(page);
- }
await page.getByTestId("topnavigation-button").click();
await expect(page.getByRole("button", { name: "User Interface" })).toBeVisible();
diff --git a/tests/plan.spec.js b/tests/plan.spec.js
index 36f8641b5c..c943c88e14 100644
--- a/tests/plan.spec.js
+++ b/tests/plan.spec.js
@@ -7,7 +7,7 @@ test.describe.configure({ mode: "parallel" });
const CONFIG = "plan.evcc.yaml";
test.beforeEach(async () => {
- await start(CONFIG, "password.sql");
+ await start(CONFIG);
});
test.afterEach(async () => {
diff --git a/tests/sessions.spec.js b/tests/sessions.spec.js
index 12227ef69e..47daa41ec6 100644
--- a/tests/sessions.spec.js
+++ b/tests/sessions.spec.js
@@ -7,7 +7,7 @@ const mobile = devices["iPhone 12 Mini"].viewport;
const desktop = devices["Desktop Chrome"].viewport;
test.beforeAll(async () => {
- await start("basics.evcc.yaml", ["password.sql", "sessions.sql"]);
+ await start("basics.evcc.yaml", "sessions.sql");
});
test.afterAll(async () => {
await stop();
diff --git a/tests/smart-cost-only.spec.js b/tests/smart-cost-only.spec.js
index bb890f70ce..06f8a5cc70 100644
--- a/tests/smart-cost-only.spec.js
+++ b/tests/smart-cost-only.spec.js
@@ -4,7 +4,7 @@ import { start, stop, baseUrl } from "./evcc";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
- await start("smart-cost-only.evcc.yaml", "password.sql");
+ await start("smart-cost-only.evcc.yaml");
});
test.afterAll(async () => {
await stop();
diff --git a/tests/smart-cost.spec.js b/tests/smart-cost.spec.js
index d9110ef37e..264b8ef892 100644
--- a/tests/smart-cost.spec.js
+++ b/tests/smart-cost.spec.js
@@ -6,7 +6,7 @@ test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
await startSimulator();
- await start(simulatorConfig(), "password.sql");
+ await start(simulatorConfig());
});
test.afterAll(async () => {
await stop();
diff --git a/tests/statistics.spec.js b/tests/statistics.spec.js
index a701e04234..a5c4400ea9 100644
--- a/tests/statistics.spec.js
+++ b/tests/statistics.spec.js
@@ -4,7 +4,7 @@ import { start, stop, baseUrl } from "./evcc";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
- await start("statistics.evcc.yaml", ["password.sql", "statistics.sql"]);
+ await start("statistics.evcc.yaml", "statistics.sql");
});
test.afterAll(async () => {
await stop();
diff --git a/tests/utils.js b/tests/utils.js
index 9a9fc2f09c..da3f4d8891 100644
--- a/tests/utils.js
+++ b/tests/utils.js
@@ -7,9 +7,3 @@ export async function enableExperimental(page) {
await page.getByRole("button", { name: "Close" }).click();
await expect(page.locator(".modal-backdrop")).not.toBeVisible();
}
-
-export async function login(page) {
- await page.locator("#loginPassword").fill("secret");
- await page.getByRole("button", { name: "Login" }).click();
- await expect(page.locator("#loginPassword")).not.toBeVisible();
-}
diff --git a/tests/vehicle-error.spec.js b/tests/vehicle-error.spec.js
index 205cdcf8bf..0db78eb00a 100644
--- a/tests/vehicle-error.spec.js
+++ b/tests/vehicle-error.spec.js
@@ -4,7 +4,7 @@ import { start, stop, baseUrl } from "./evcc";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
- await start("vehicle-error.evcc.yaml", "password.sql");
+ await start("vehicle-error.evcc.yaml");
});
test.afterAll(async () => {
await stop();
diff --git a/tests/vehicle-settings.spec.js b/tests/vehicle-settings.spec.js
index 86b8199fd8..9b514c6a1a 100644
--- a/tests/vehicle-settings.spec.js
+++ b/tests/vehicle-settings.spec.js
@@ -12,7 +12,7 @@ test.afterAll(async () => {
});
test.beforeEach(async ({ page }) => {
- await start(simulatorConfig(), "password.sql");
+ await start(simulatorConfig());
await page.goto(simulatorUrl());
await page.getByLabel("Grid Power").fill("500");
diff --git a/util/auth/auth.go b/util/auth/auth.go
index 1274137efb..a6cd4f7ef0 100644
--- a/util/auth/auth.go
+++ b/util/auth/auth.go
@@ -22,18 +22,21 @@ type Auth interface {
GenerateJwtToken(time.Duration) (string, error)
ValidateJwtToken(string) (bool, error)
IsAdminPasswordConfigured() bool
+ Disable()
+ Disabled() bool
}
type auth struct {
settings settings.API
+ disabled bool
}
func New() Auth {
- return &auth{settings: new(settings.Settings)}
+ return &auth{settings: new(settings.Settings), disabled: false}
}
func NewMock(settings settings.API) Auth {
- return &auth{settings: settings}
+ return &auth{settings: settings, disabled: false}
}
func (a *auth) hashPassword(password string) (string, error) {
@@ -140,3 +143,11 @@ func (a *auth) ValidateJwtToken(tokenString string) (bool, error) {
return true, nil
}
+
+func (a *auth) Disable() {
+ a.disabled = true
+}
+
+func (a *auth) Disabled() bool {
+ return a.disabled
+}
diff --git a/util/templates/defaults.yaml b/util/templates/defaults.yaml
index 881bf0d14b..b3a6177981 100644
--- a/util/templates/defaults.yaml
+++ b/util/templates/defaults.yaml
@@ -277,6 +277,14 @@ params:
help:
en: The AIN is printed on the type label on the back of the device. Embed it in double quotes in case of leading zeroes.
de: Die AIN ist auf dem Typenschild auf der Geräterückseite aufgedruckt. Bei führenden Nullen bitte in doppelte Hochkommata setzen.
+ - name: coarsecurrent
+ type: bool
+ help:
+ en: 1A current control
+ de: 1A Ladestromvorgabe
+ description:
+ en: Vehicle supports 1A current steps only
+ de: Fahrzeug unterstützt nur 1A Schritte der Ladestromvorgabe
- name: welcomecharge
type: bool
description:
@@ -325,16 +333,19 @@ presets:
params:
- name: charges
type: number
+ advanced: true
help:
de: Zusätzlicher fester Aufschlag pro kWh (z.B. 0.05 für 5 Cent)
en: Additional fixed charge per kWh (e.g. 0.05 for 5 cents)
- name: tax
type: number
+ advanced: true
help:
de: Zusätzlicher prozentualer Aufschlag (z.B. 0.2 für 20%)
en: Additional percentage charge (e.g. 0.2 for 20%)
- name: formula
type: string
+ advanced: true
help:
de: Individuelle Formel zur Berechnung des Preises
en: Individual formula for calculating the price
diff --git a/vehicle/polestar/identity.go b/vehicle/polestar/identity.go
index 9c5e18fc16..9fb683e267 100644
--- a/vehicle/polestar/identity.go
+++ b/vehicle/polestar/identity.go
@@ -24,7 +24,7 @@ const OAuthURI = "https://polestarid.eu.polestar.com"
// https://polestarid.eu.polestar.com/.well-known/openid-configuration
var OAuth2Config = &oauth2.Config{
- ClientID: "polmystar",
+ ClientID: "l3oopkc_10",
RedirectURL: "https://www.polestar.com/sign-in-callback",
Endpoint: oauth2.Endpoint{
AuthURL: OAuthURI + "/as/authorization.oauth2",
@@ -84,18 +84,30 @@ func (v *Identity) login() (*oauth2.Token, error) {
}
uri = fmt.Sprintf("%s/as/%s/resume/as/authorization.ping?client_id=%s", OAuthURI, resume, OAuth2Config.ClientID)
- v.Client.CheckRedirect, param = request.InterceptRedirect("code", true)
- defer func() { v.Client.CheckRedirect = nil }()
var code string
- if _, err = v.Post(uri, request.FormContent, strings.NewReader(params.Encode())); err == nil {
- code, err = param()
+ var uid string
+ v.Client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
+ code = req.URL.Query().Get("code")
+ uid = req.URL.Query().Get("uid")
+ return nil
}
+ defer func() { v.Client.CheckRedirect = nil }()
- if err != nil {
+ if _, err := v.Post(uri, request.FormContent, strings.NewReader(params.Encode())); err != nil {
return nil, err
}
+ // If the authorization code is empty, this indicates that user consent must be handled
+ // before the code can be obtained. The `confirmConsentAndGetCode` method is called as a
+ // workaround to guide the user through the consent process and retrieve the authorization code.
+ if code == "" {
+ code, err = v.confirmConsentAndGetCode(resume, uid)
+ if err != nil {
+ return nil, err
+ }
+ }
+
var res struct {
Token `graphql:"getAuthToken(code: $code)"`
}
@@ -137,3 +149,35 @@ func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) {
return v.login()
}
+
+func (v *Identity) confirmConsentAndGetCode(resume, uid string) (string, error) {
+ // Extract the user ID (UID) from the redirect parameters
+ if uid == "" {
+ return "", fmt.Errorf("failed to extract user ID")
+ }
+
+ // Confirm user consent by submitting the consent form, which rejects cookies
+ data := url.Values{
+ "pf.submit": []string{"true"},
+ "subject": []string{uid},
+ }
+
+ // Retrieve the authorization code after consent has been confirmed
+ var param request.InterceptResult
+ v.Client.CheckRedirect, param = request.InterceptRedirect("code", true)
+ defer func() { v.Client.CheckRedirect = nil }()
+
+ // Make a POST request to confirm the user consent
+ if _, err := v.Post(fmt.Sprintf("%s/as/%s/resume/as/authorization.ping", OAuthURI, resume), request.FormContent, strings.NewReader(data.Encode())); err != nil {
+ return "", fmt.Errorf("failed confirming user consent: %w", err)
+ }
+
+ // Extract the authorization code from the response
+ code, err := param()
+ if err != nil || code == "" {
+ return "", fmt.Errorf("failed extracting authorisation code: %w", err)
+ }
+
+ // Return the retrieved code
+ return code, nil
+}