From d78d4bda0213e9109a923ca0d0c810fa0bfc6bd9 Mon Sep 17 00:00:00 2001 From: andig Date: Tue, 7 Jan 2025 10:54:22 +0100 Subject: [PATCH 1/6] Tariff: fix memory leak when using formula (#18098) --- provider/golang/registry.go | 13 +------------ tariff/embed.go | 36 ++++++++++++------------------------ 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/provider/golang/registry.go b/provider/golang/registry.go index 21d024f61c..72f0bcf235 100644 --- a/provider/golang/registry.go +++ b/provider/golang/registry.go @@ -8,13 +8,6 @@ import ( "github.com/traefik/yaegi/interp" ) -const Imports = `import ( - "fmt" - "math" - "strings" - "time" -)` - var ( mu sync.Mutex registry = make(map[string]*interp.Interpreter) @@ -31,14 +24,10 @@ func RegisteredVM(name, init string) (*interp.Interpreter, error) { // create new VM if !ok { vm = interp.New(interp.Options{}) - if err := vm.Use(stdlib.Symbols); err != nil { return nil, err } - - if _, err := vm.Eval(Imports); err != nil { - return nil, err - } + vm.ImportUsed() if init != "" { if _, err := vm.Eval(init); err != nil { diff --git a/tariff/embed.go b/tariff/embed.go index c9c523a39a..effc0213a7 100644 --- a/tariff/embed.go +++ b/tariff/embed.go @@ -5,7 +5,6 @@ import ( "fmt" "time" - "github.com/evcc-io/evcc/provider/golang" "github.com/evcc-io/evcc/provider/golang/stdlib" "github.com/traefik/yaegi/interp" ) @@ -29,35 +28,24 @@ func (t *embed) init() (err error) { return nil } - vm := interp.New(interp.Options{}) - if err := vm.Use(stdlib.Symbols); err != nil { - return err - } - - if _, err := vm.Eval(fmt.Sprintf(`%s - var ( - price float64 - charges float64 = %f - tax float64 = %f - ts time.Time - )`, golang.Imports, t.Charges, t.Tax)); err != nil { - return err - } - - prg, err := vm.Compile(t.Formula) - if err != nil { - return err - } - t.calc = func(price float64, ts time.Time) (float64, error) { + vm := interp.New(interp.Options{}) + if err := vm.Use(stdlib.Symbols); err != nil { + return 0, err + } + vm.ImportUsed() + if _, err := vm.Eval(fmt.Sprintf(` - price = %f + var ( + price float64 = %f + charges float64 = %f + tax float64 = %f ts = time.Unix(%d, 0).Local() - `, price, ts.Unix())); err != nil { + )`, price, t.Charges, t.Tax, ts.Unix())); err != nil { return 0, err } - res, err := vm.Execute(prg) + res, err := vm.Eval(t.Formula) if err != nil { return 0, err } From a24b8b89f57882e62969e77068d2af99dc12fdb9 Mon Sep 17 00:00:00 2001 From: premultiply <4681172+premultiply@users.noreply.github.com> Date: Tue, 7 Jan 2025 10:56:05 +0100 Subject: [PATCH 2/6] ABL eMH: force re-enable outlet (#17950) --- charger/abl.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/charger/abl.go b/charger/abl.go index 9a5953b2e9..c2f0f57c9f 100644 --- a/charger/abl.go +++ b/charger/abl.go @@ -3,7 +3,7 @@ package charger // LICENSE // Copyright (c) 2019-2022 andig -// Copyright (c) 2022 premultiply +// Copyright (c) 2022-2024 premultiply // This module is NOT covered by the MIT license. All rights reserved. @@ -157,15 +157,20 @@ func (wb *ABLeMH) Status() (api.ChargeStatus, error) { return api.StatusNone, err } - r := rune(b[1]>>4-0x0A) + 'A' + s := string(rune((b[1]>>4)-0x0A) + 'A') - switch r { - case 'A', 'B', 'C': - return api.ChargeStatus(r), nil + switch s { + case "A", "B", "C": + return api.ChargeStatusString(s) default: + // ensure Outlet is re-enabled after wake-up + if b[1] == 0xE0 { // Outlet is disabled + return api.StatusB, wb.set(ablRegModifyState, 0xA1A1) + } + status, ok := ablStatus[b[1]] if !ok { - status = string(r) + status = s } return api.StatusNone, fmt.Errorf("invalid status: %s", status) From 91c0ab20be264c01ef9eeeb6cf8c871015fa6375 Mon Sep 17 00:00:00 2001 From: andig Date: Tue, 7 Jan 2025 16:54:21 +0100 Subject: [PATCH 3/6] Zendure: fix meter updates (#18116) --- meter/zendure/connection.go | 4 +--- meter/zendure/connection_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 meter/zendure/connection_test.go diff --git a/meter/zendure/connection.go b/meter/zendure/connection.go index da81946995..f1c7de9fe3 100644 --- a/meter/zendure/connection.go +++ b/meter/zendure/connection.go @@ -73,12 +73,10 @@ func (c *Connection) handler(data string) { } c.data.SetFunc(func(v Data) Data { - if err := mergo.Merge(&v, res.Data); err != nil { + if err := mergo.Merge(&v, res.Data, mergo.WithOverride); err != nil { c.log.ERROR.Println(err) } - c.log.TRACE.Printf("!! data: %+v", v) - return v }) } diff --git a/meter/zendure/connection_test.go b/meter/zendure/connection_test.go new file mode 100644 index 0000000000..12eb9bfb21 --- /dev/null +++ b/meter/zendure/connection_test.go @@ -0,0 +1,31 @@ +package zendure + +import ( + "testing" + "time" + + "github.com/evcc-io/evcc/util" + "github.com/stretchr/testify/assert" +) + +func TestHandler(t *testing.T) { + conn := &Connection{ + log: util.NewLogger("test"), + data: util.NewMonitor[Data](time.Minute), + } + + { + conn.handler(`{"solarInputPower":113,"sn":"serial"}`) + res, err := conn.data.Get() + assert.NoError(t, err) + + assert.Equal(t, Data{SolarInputPower: 113, Sn: "serial"}, res) + } + { + conn.handler(`{"solarInputPower":125,"sn":"serial"}`) + res, err := conn.data.Get() + assert.NoError(t, err) + + assert.Equal(t, Data{SolarInputPower: 125, Sn: "serial"}, res) + } +} From 65bdb404941483d8340d81c92505741763a4d930 Mon Sep 17 00:00:00 2001 From: andig Date: Tue, 7 Jan 2025 18:22:50 +0100 Subject: [PATCH 4/6] chore: fix phase configuration (#18062) --- assets/js/components/Loadpoint.vue | 4 ++-- .../js/components/LoadpointSettingsModal.vue | 20 +++++++++---------- core/keys/loadpoint.go | 4 ++-- core/loadpoint.go | 9 ++------- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/assets/js/components/Loadpoint.vue b/assets/js/components/Loadpoint.vue index 90042216cc..b2c3ada9cd 100644 --- a/assets/js/components/Loadpoint.vue +++ b/assets/js/components/Loadpoint.vue @@ -183,8 +183,8 @@ export default { phases: Number, phasesConfigured: Number, phasesActive: Number, - chargerPhases1p3p: Boolean, - chargerPhysicalPhases: Number, + chargerPhaseSwitching: Boolean, + chargerPhaseReading: Boolean, minCurrent: Number, maxCurrent: Number, chargeCurrent: Number, diff --git a/assets/js/components/LoadpointSettingsModal.vue b/assets/js/components/LoadpointSettingsModal.vue index 2fd21aedf0..6b4506c896 100644 --- a/assets/js/components/LoadpointSettingsModal.vue +++ b/assets/js/components/LoadpointSettingsModal.vue @@ -47,7 +47,7 @@ {{ $t("main.loadpointSettings.phasesConfigured.label") }}
-

+

{{ $t( @@ -184,8 +184,8 @@ export default { id: [String, Number], phasesConfigured: Number, phasesActive: Number, - chargerPhases1p3p: Boolean, - chargerPhysicalPhases: Number, + chargerPhaseSwitching: Boolean, + chargerPhaseReading: Boolean, batteryBoost: Boolean, batteryBoostAvailable: Boolean, mode: String, @@ -215,14 +215,14 @@ export default { }, computed: { phasesOptions() { - if (this.chargerPhysicalPhases == 1) { - // known fixed phase configuration, no settings required - return []; - } - if (this.chargerPhases1p3p) { + if (this.chargerPhaseSwitching) { // automatic switching return [PHASES_AUTO, PHASES_3, PHASES_1]; } + if (this.chargerPhaseReading) { + // known fixed phase configuration, no settings required + return []; + } // 1p or 3p possible return [PHASES_3, PHASES_1]; }, @@ -230,7 +230,7 @@ export default { return this.collectProps(LoadpointSettingsBatteryBoost); }, maxPower() { - if (this.chargerPhases1p3p) { + if (this.chargerPhaseSwitching) { if (this.phasesConfigured === PHASES_AUTO) { return this.maxPowerPhases(3); } @@ -241,7 +241,7 @@ export default { return this.fmtW(this.maxCurrent * V * this.phasesActive); }, minPower() { - if (this.chargerPhases1p3p) { + if (this.chargerPhaseSwitching) { if (this.phasesConfigured === PHASES_AUTO) { return this.minPowerPhases(1); } diff --git a/core/keys/loadpoint.go b/core/keys/loadpoint.go index 4308fddf3e..f9966df848 100644 --- a/core/keys/loadpoint.go +++ b/core/keys/loadpoint.go @@ -22,8 +22,8 @@ const ( ChargerIcon = "chargerIcon" // charger icon for ui ChargerFeature = "chargerFeature" // charger feature - ChargerPhysicalPhases = "chargerPhysicalPhases" // charger phases - ChargerPhases1p3p = "chargerPhases1p3p" // phase switcher (1p3p chargers) + ChargerPhaseSwitching = "chargerPhaseSwitching" // api.PhaseSwitcher: phase switcher (1p3p chargers) available + ChargerPhaseReading = "chargerPhaseReading" // api.PhaseDescriber: get charger phases available ChargerStatusReason = "chargerStatusReason" // either awaiting authorization or disconnect required // loadpoint status diff --git a/core/loadpoint.go b/core/loadpoint.go index 906a4c39bf..04de9969e7 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -624,18 +624,13 @@ func (lp *Loadpoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even lp.publish(keys.DisableDelay, lp.Disable.Delay) lp.publish(keys.PhasesConfigured, lp.configuredPhases) - lp.publish(keys.ChargerPhases1p3p, lp.hasPhaseSwitching()) + lp.publish(keys.ChargerPhaseSwitching, lp.hasPhaseSwitching()) + lp.publish(keys.ChargerPhaseReading, lp.getChargerPhysicalPhases() != 0) lp.publish(keys.PhasesEnabled, lp.phases) lp.publish(keys.PhasesActive, lp.ActivePhases()) lp.publishTimer(phaseTimer, 0, timerInactive) lp.publishTimer(pvTimer, 0, timerInactive) - if phases := lp.getChargerPhysicalPhases(); phases != 0 { - lp.publish(keys.ChargerPhysicalPhases, phases) - } else { - lp.publish(keys.ChargerPhysicalPhases, nil) - } - // charger features for _, f := range []api.Feature{api.IntegratedDevice, api.Heating} { lp.publishChargerFeature(f) From 54b6b911cefb645622eb4c3187546350ddac03b6 Mon Sep 17 00:00:00 2001 From: mfuchs1984 <57141790+mfuchs1984@users.noreply.github.com> Date: Tue, 7 Jan 2025 19:43:58 +0100 Subject: [PATCH 5/6] BMW: add climater (#18114) --- vehicle/bmw/provider.go | 8 ++++++++ vehicle/bmw/types.go | 3 +++ 2 files changed, 11 insertions(+) diff --git a/vehicle/bmw/provider.go b/vehicle/bmw/provider.go index 6978347b2b..3c65beee9a 100644 --- a/vehicle/bmw/provider.go +++ b/vehicle/bmw/provider.go @@ -108,6 +108,14 @@ func (v *Provider) GetLimitSoc() (int64, error) { return res.State.ElectricChargingState.ChargingTarget, nil } +var _ api.VehicleClimater = (*Provider)(nil) + +// Climater implements the api.VehicleClimater interface +func (v *Provider) Climater() (bool, error) { + res, err := v.statusG() + return res.State.ClimateControlState.Activity == "HEATING" || res.State.ClimateControlState.Activity == "COOLING", err +} + var _ api.Resurrector = (*Provider)(nil) func (v *Provider) WakeUp() error { diff --git a/vehicle/bmw/types.go b/vehicle/bmw/types.go index 6d2f7aa7ca..5f8f07ec64 100644 --- a/vehicle/bmw/types.go +++ b/vehicle/bmw/types.go @@ -19,5 +19,8 @@ type VehicleStatus struct { ChargingStatus string ChargingTarget int64 } + ClimateControlState struct { + Activity string + } } } From e9d67ee0069a3c088ff9ecbb504a3c0108e64d6d Mon Sep 17 00:00:00 2001 From: andig Date: Tue, 7 Jan 2025 22:22:24 +0100 Subject: [PATCH 6/6] Revert "chore: fix phase configuration (#18062)" This reverts commit 65bdb404941483d8340d81c92505741763a4d930. --- assets/js/components/Loadpoint.vue | 4 ++-- .../js/components/LoadpointSettingsModal.vue | 20 +++++++++---------- core/keys/loadpoint.go | 4 ++-- core/loadpoint.go | 9 +++++++-- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/assets/js/components/Loadpoint.vue b/assets/js/components/Loadpoint.vue index b2c3ada9cd..90042216cc 100644 --- a/assets/js/components/Loadpoint.vue +++ b/assets/js/components/Loadpoint.vue @@ -183,8 +183,8 @@ export default { phases: Number, phasesConfigured: Number, phasesActive: Number, - chargerPhaseSwitching: Boolean, - chargerPhaseReading: Boolean, + chargerPhases1p3p: Boolean, + chargerPhysicalPhases: Number, minCurrent: Number, maxCurrent: Number, chargeCurrent: Number, diff --git a/assets/js/components/LoadpointSettingsModal.vue b/assets/js/components/LoadpointSettingsModal.vue index 6b4506c896..2fd21aedf0 100644 --- a/assets/js/components/LoadpointSettingsModal.vue +++ b/assets/js/components/LoadpointSettingsModal.vue @@ -47,7 +47,7 @@ {{ $t("main.loadpointSettings.phasesConfigured.label") }}

-

+

{{ $t( @@ -184,8 +184,8 @@ export default { id: [String, Number], phasesConfigured: Number, phasesActive: Number, - chargerPhaseSwitching: Boolean, - chargerPhaseReading: Boolean, + chargerPhases1p3p: Boolean, + chargerPhysicalPhases: Number, batteryBoost: Boolean, batteryBoostAvailable: Boolean, mode: String, @@ -215,14 +215,14 @@ export default { }, computed: { phasesOptions() { - if (this.chargerPhaseSwitching) { - // automatic switching - return [PHASES_AUTO, PHASES_3, PHASES_1]; - } - if (this.chargerPhaseReading) { + if (this.chargerPhysicalPhases == 1) { // known fixed phase configuration, no settings required return []; } + if (this.chargerPhases1p3p) { + // automatic switching + return [PHASES_AUTO, PHASES_3, PHASES_1]; + } // 1p or 3p possible return [PHASES_3, PHASES_1]; }, @@ -230,7 +230,7 @@ export default { return this.collectProps(LoadpointSettingsBatteryBoost); }, maxPower() { - if (this.chargerPhaseSwitching) { + if (this.chargerPhases1p3p) { if (this.phasesConfigured === PHASES_AUTO) { return this.maxPowerPhases(3); } @@ -241,7 +241,7 @@ export default { return this.fmtW(this.maxCurrent * V * this.phasesActive); }, minPower() { - if (this.chargerPhaseSwitching) { + if (this.chargerPhases1p3p) { if (this.phasesConfigured === PHASES_AUTO) { return this.minPowerPhases(1); } diff --git a/core/keys/loadpoint.go b/core/keys/loadpoint.go index f9966df848..4308fddf3e 100644 --- a/core/keys/loadpoint.go +++ b/core/keys/loadpoint.go @@ -22,8 +22,8 @@ const ( ChargerIcon = "chargerIcon" // charger icon for ui ChargerFeature = "chargerFeature" // charger feature - ChargerPhaseSwitching = "chargerPhaseSwitching" // api.PhaseSwitcher: phase switcher (1p3p chargers) available - ChargerPhaseReading = "chargerPhaseReading" // api.PhaseDescriber: get charger phases available + ChargerPhysicalPhases = "chargerPhysicalPhases" // charger phases + ChargerPhases1p3p = "chargerPhases1p3p" // phase switcher (1p3p chargers) ChargerStatusReason = "chargerStatusReason" // either awaiting authorization or disconnect required // loadpoint status diff --git a/core/loadpoint.go b/core/loadpoint.go index 04de9969e7..906a4c39bf 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -624,13 +624,18 @@ func (lp *Loadpoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even lp.publish(keys.DisableDelay, lp.Disable.Delay) lp.publish(keys.PhasesConfigured, lp.configuredPhases) - lp.publish(keys.ChargerPhaseSwitching, lp.hasPhaseSwitching()) - lp.publish(keys.ChargerPhaseReading, lp.getChargerPhysicalPhases() != 0) + lp.publish(keys.ChargerPhases1p3p, lp.hasPhaseSwitching()) lp.publish(keys.PhasesEnabled, lp.phases) lp.publish(keys.PhasesActive, lp.ActivePhases()) lp.publishTimer(phaseTimer, 0, timerInactive) lp.publishTimer(pvTimer, 0, timerInactive) + if phases := lp.getChargerPhysicalPhases(); phases != 0 { + lp.publish(keys.ChargerPhysicalPhases, phases) + } else { + lp.publish(keys.ChargerPhysicalPhases, nil) + } + // charger features for _, f := range []api.Feature{api.IntegratedDevice, api.Heating} { lp.publishChargerFeature(f)