From 4360914d75f0beec5951d458cb4f8803b332aebf Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 16 Nov 2023 16:55:43 +0100 Subject: [PATCH 1/5] Add chargeable battery charger --- charger/battery.go | 96 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 charger/battery.go diff --git a/charger/battery.go b/charger/battery.go new file mode 100644 index 0000000000..6f0797c40b --- /dev/null +++ b/charger/battery.go @@ -0,0 +1,96 @@ +package charger + +import ( + "fmt" + + "github.com/evcc-io/evcc/api" + "github.com/evcc-io/evcc/util" + "github.com/evcc-io/evcc/util/config" +) + +type ChargeableBattery interface { + api.Meter + api.Battery + api.BatteryController +} + +// Battery implements an api.Charger for controllable batteries +type Battery struct { + *embed + ChargeableBattery + mode api.BatteryMode +} + +func init() { + registry.Add("battery", NewBatteryFromConfig) +} + +func NewBatteryFromConfig(other map[string]interface{}) (api.Charger, error) { + cc := struct { + embed `mapstructure:",squash"` + Battery string + }{ + embed: embed{ + Icon_: "battery", + Features_: []api.Feature{api.IntegratedDevice}, + }, + } + + if err := util.DecodeOther(other, &cc); err != nil { + return nil, err + } + + dev, err := config.Meters().ByName(cc.Battery) + if err != nil { + return nil, err + } + + battery, ok := dev.Instance().(ChargeableBattery) + if !ok { + return nil, fmt.Errorf("'%s' is not a chargeable battery", cc.Battery) + } + + c := &Battery{ + ChargeableBattery: battery, + } + + return c, nil +} + +// Status calculates the battery charging status +func (c *Battery) Status() (api.ChargeStatus, error) { + res := api.StatusB + + p, err := c.ChargeableBattery.CurrentPower() + if p > 0 && err == nil { + res = api.StatusC + } + + return res, err +} + +// Enabled implements the api.Charger interface +func (c *Battery) Enabled() (bool, error) { + return c.mode == api.BatteryCharge, nil +} + +// Enable implements the api.Charger interface +func (c *Battery) Enable(enable bool) error { + mode := api.BatteryNormal + if enable { + mode = api.BatteryCharge + } + + // TODO handle locking of battery + err := c.ChargeableBattery.SetBatteryMode(mode) + if err == nil { + c.mode = mode + } + + return err +} + +// MaxCurrent implements the api.Charger interface +func (c *Battery) MaxCurrent(current int64) error { + return nil +} From 8fc252fec72352b21d0d8358f8793224a7a363d8 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 21 Mar 2024 19:40:36 +0100 Subject: [PATCH 2/5] wip --- charger/battery.go | 1 + 1 file changed, 1 insertion(+) diff --git a/charger/battery.go b/charger/battery.go index 6f0797c40b..471c336d9b 100644 --- a/charger/battery.go +++ b/charger/battery.go @@ -51,6 +51,7 @@ func NewBatteryFromConfig(other map[string]interface{}) (api.Charger, error) { } c := &Battery{ + embed: &cc.embed, ChargeableBattery: battery, } From 76c738130590f6ebc73dbec7399481fe482e228d Mon Sep 17 00:00:00 2001 From: andig Date: Sat, 23 Mar 2024 09:55:33 +0100 Subject: [PATCH 3/5] Flip power sign --- charger/battery.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/charger/battery.go b/charger/battery.go index 471c336d9b..047493789c 100644 --- a/charger/battery.go +++ b/charger/battery.go @@ -95,3 +95,11 @@ func (c *Battery) Enable(enable bool) error { func (c *Battery) MaxCurrent(current int64) error { return nil } + +var _ api.Meter = (*Battery)(nil) + +// CurrentPower implements the api.Meter interface +func (c *Battery) CurrentPower() (float64, error) { + res, err := c.ChargeableBattery.CurrentPower() + return -res, err +} From 642b666841ffd8f6909b08ec5c0a465eb1dc1241 Mon Sep 17 00:00:00 2001 From: andig Date: Sat, 23 Mar 2024 09:59:34 +0100 Subject: [PATCH 4/5] Cap power at 0 --- charger/battery.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charger/battery.go b/charger/battery.go index 047493789c..40d87ae504 100644 --- a/charger/battery.go +++ b/charger/battery.go @@ -101,5 +101,5 @@ var _ api.Meter = (*Battery)(nil) // CurrentPower implements the api.Meter interface func (c *Battery) CurrentPower() (float64, error) { res, err := c.ChargeableBattery.CurrentPower() - return -res, err + return max(-res, 0), err } From 298ea702efb50e3dd10cebd97f5cce34f633c258 Mon Sep 17 00:00:00 2001 From: andig Date: Fri, 29 Mar 2024 21:47:05 +0100 Subject: [PATCH 5/5] Fix disabled but charging --- charger/battery.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/charger/battery.go b/charger/battery.go index 40d87ae504..66e42d841e 100644 --- a/charger/battery.go +++ b/charger/battery.go @@ -100,6 +100,10 @@ var _ api.Meter = (*Battery)(nil) // CurrentPower implements the api.Meter interface func (c *Battery) CurrentPower() (float64, error) { + if c.mode != api.BatteryCharge { + // disabled + return 0, nil + } res, err := c.ChargeableBattery.CurrentPower() return max(-res, 0), err }