Skip to content

Commit

Permalink
Merge pull request #8 from atbore-phx/feat/reserve-battery-capacity
Browse files Browse the repository at this point in the history
Feat/reserve battery capacity
  • Loading branch information
atbore-phx authored Mar 5, 2024
2 parents fb5224e + 79f10c7 commit 935f75f
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 37 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
- pull_request

jobs:
build:
tests:
name: Tests
runs-on: ubuntu-latest
steps:
Expand All @@ -18,4 +18,6 @@ jobs:
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: atbore-phx/sbam
slug: atbore-phx/sbam
- name: Build
run: make build
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ VERSION=$(shell git describe --tags --always)
COMMIT=$(shell git rev-parse --short HEAD)
DATE=$(shell date)

.PHONY: build test
.PHONY: build test test-build

test:
go test ./...

build: test
build:
rm -rf bin
mkdir -p bin
CGO_ENABLED=0 go build -ldflags="-X 'main.version=$(VERSION)' -X 'main.commit=$(COMMIT)' -X 'main.date=$(DATE)'" -o bin/sbam

test-build: test build
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ Flags:
-e, --end_hr string END_HR (default "06:00")
-H, --fronius_ip string FRONIUS_IP
-h, --help help for schedule
-m, --max_charge int MAX_CHARGE (default 3500)
-m, --max_charge float MAX_CHARGE (default 3500)
-r, --pw_batt_reserve float PW_BATT_RESERVE
-c, --pw_consumption float PW_CONSUMPTION
-s, --start_hr string START_HR (default "00:00")
-u, --url string URL
Expand Down
10 changes: 7 additions & 3 deletions pkg/cmd/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ var scdCmd = &cobra.Command{
pw_consumption := viper.GetFloat64("pw_consumption")
start_hr := viper.GetString("start_hr")
end_hr := viper.GetString("end_hr")
max_charge := viper.GetInt("max_charge")
max_charge := viper.GetFloat64("max_charge")
pw_batt_reserve := viper.GetFloat64("pw_batt_reserve")

if len(strings.TrimSpace(fronius_ip)) == 0 {
fmt.Println("The --fronius_ip flag must be set")
Expand Down Expand Up @@ -54,7 +55,7 @@ var scdCmd = &cobra.Command{
u.Log.Infof("your Daily consumption is:%d W", int(pw_consumption))

scd := fronius.New()
_, err = scd.Handler(solarPowerProduction, capacity2charge, capacity_max, pw_consumption, max_charge, start_hr, end_hr, fronius_ip)
_, err = scd.Handler(solarPowerProduction, capacity2charge, capacity_max, pw_consumption, max_charge, pw_batt_reserve, start_hr, end_hr, fronius_ip)
if err != nil {
panic(err)
}
Expand All @@ -68,7 +69,8 @@ func init() {
scdCmd.Flags().StringP("start_hr", "s", "00:00", "START_HR")
scdCmd.Flags().StringP("end_hr", "e", "06:00", "END_HR")
scdCmd.Flags().Float64P("pw_consumption", "c", 0.0, "PW_CONSUMPTION")
scdCmd.Flags().IntP("max_charge", "m", 3500, "MAX_CHARGE")
scdCmd.Flags().Float64P("max_charge", "m", 3500, "MAX_CHARGE")
scdCmd.Flags().Float64P("pw_batt_reserve", "r", 0, "PW_BATT_RESERVE")

viper.BindPFlag("url", scdCmd.Flags().Lookup("url"))
viper.BindPFlag("apikey", scdCmd.Flags().Lookup("apikey"))
Expand All @@ -77,6 +79,8 @@ func init() {
viper.BindPFlag("start_hr", scdCmd.Flags().Lookup("start_hr"))
viper.BindPFlag("end_hr", scdCmd.Flags().Lookup("end_hr"))
viper.BindPFlag("max_charge", scdCmd.Flags().Lookup("max_charge"))
viper.BindPFlag("pw_batt_reserve", scdCmd.Flags().Lookup("pw_batt_reserve"))

rootCmd.AddCommand(scdCmd)
}

Expand Down
24 changes: 18 additions & 6 deletions pkg/fronius/fronius_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,13 @@ func TestHandler(t *testing.T) {
pwBatt2charge := 1000.0
pwBattMax := 10000.0
pwConsumption := 9000.0
maxCharge := 3500
maxCharge := 3500.0
pw_batt_reserve := 0.0
startHr := "09:00"
endHr := "17:00"

setup()
_, err := fronius.Handler(pwForecast, pwBatt2charge, pwBattMax, pwConsumption, maxCharge, startHr, endHr, modbus_ip, modbus_port)
_, err := fronius.Handler(pwForecast, pwBatt2charge, pwBattMax, pwConsumption, maxCharge, pw_batt_reserve, startHr, endHr, modbus_ip, modbus_port)
teardown()

assert.NoError(err, "Handler returned an error")
Expand Down Expand Up @@ -149,7 +150,7 @@ func TestCheckTimeRange(t *testing.T) {
func TestBatteryChargeMode1(t *testing.T) {
assert := assert.New(t)
setup()
result, err := fronius.SetFroniusChargeBatteryMode(1000, 0, 11000, 9000, 3500, "00:00", "05:00", modbus_ip, modbus_port)
result, err := fronius.SetFroniusChargeBatteryMode(1000, 0, 11000, 9000, 3500, 0, "00:00", "05:00", modbus_ip, modbus_port)
assert.Equal(int16(0), result, "SetFroniusChargeBatteryMode returned wrong value")
assert.NoError(err)

Expand All @@ -160,7 +161,7 @@ func TestBatteryChargeMode2(t *testing.T) {
assert := assert.New(t)
setup()

result, err := fronius.SetFroniusChargeBatteryMode(1000, 11000, 11000, 9000, 3500, "00:00", "23:59", modbus_ip, modbus_port)
result, err := fronius.SetFroniusChargeBatteryMode(1000, 11000, 11000, 9000, 3500, 0, "00:00", "23:59", modbus_ip, modbus_port)
assert.Equal(int16(31), result, "SetFroniusChargeBatteryMode returned wrong value")
assert.NoError(err)

Expand All @@ -171,7 +172,7 @@ func TestBatteryChargeMode3(t *testing.T) {
assert := assert.New(t)
setup()

result, err := fronius.SetFroniusChargeBatteryMode(10000, 5000, 11000, 9000, 3500, "00:00", "23:59", modbus_ip, modbus_port)
result, err := fronius.SetFroniusChargeBatteryMode(10000, 5000, 11000, 9000, 3500, 0, "00:00", "23:59", modbus_ip, modbus_port)
assert.Equal(int16(0), result, "SetFroniusChargeBatteryMode returned wrong value")
assert.NoError(err)

Expand All @@ -182,9 +183,20 @@ func TestBatteryChargeMode4(t *testing.T) {
assert := assert.New(t)
setup()

result, err := fronius.SetFroniusChargeBatteryMode(10000, 0, 11000, 9000, 3500, "00:00", "23:59", modbus_ip, modbus_port)
result, err := fronius.SetFroniusChargeBatteryMode(10000, 0, 11000, 9000, 3500, 0, "00:00", "23:59", modbus_ip, modbus_port)
assert.Equal(int16(0), result, "SetFroniusChargeBatteryMode returned wrong value")
assert.NoError(err)

teardown()
}

func TestBatteryChargeMode5(t *testing.T) {
assert := assert.New(t)
setup()

result, err := fronius.SetFroniusChargeBatteryMode(1000, 11000, 11000, 9000, 3500, 2500, "00:00", "23:59", modbus_ip, modbus_port)
assert.Equal(int16(22), result, "SetFroniusChargeBatteryMode returned wrong value")
assert.NoError(err)

teardown()
}
4 changes: 2 additions & 2 deletions pkg/fronius/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ func New() *Fronius {
return &Fronius{}
}

func (fronius *Fronius) Handler(pw_forecast float64, pw_batt2charge float64, pw_batt_max float64, pw_consumption float64, max_charge int, start_hr string, end_hr string, fronius_ip string, fronius_port ...string) (int16, error) {
func (fronius *Fronius) Handler(pw_forecast float64, pw_batt2charge float64, pw_batt_max float64, pw_consumption float64, max_charge float64, pw_batt_reserve float64, start_hr string, end_hr string, fronius_ip string, fronius_port ...string) (int16, error) {
p := "502"
if len(fronius_port) > 0 {
p = fronius_port[0]
}

charge_pc, _ := SetFroniusChargeBatteryMode(pw_forecast, pw_batt2charge, pw_batt_max, pw_consumption, max_charge, start_hr, end_hr, fronius_ip, p)
charge_pc, _ := SetFroniusChargeBatteryMode(pw_forecast, pw_batt2charge, pw_batt_max, pw_consumption, max_charge, pw_batt_reserve, start_hr, end_hr, fronius_ip, p)

return charge_pc, nil

Expand Down
60 changes: 39 additions & 21 deletions pkg/fronius/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,39 @@ import (
"time"
)

func SetFroniusChargeBatteryMode(pw_forecast float64, pw_batt2charge float64, pw_batt_max float64, pw_consumption float64, max_charge int, start_hr string, end_hr string, fronius_ip string, fronius_port ...string) (int16, error) {
func SetFroniusChargeBatteryMode(pw_forecast float64, pw_batt2charge float64, pw_batt_max float64, pw_consumption float64, max_charge float64, pw_batt_reserve float64, start_hr string, end_hr string, fronius_ip string, fronius_port ...string) (int16, error) {
p := "502"
if len(fronius_port) > 0 {
p = fronius_port[0]
}
var ch_pc int16 = 0
pw_grid := 0.0
pw_pv_net := pw_forecast - pw_consumption // Net solar power
pw_batt := pw_batt_max - pw_batt2charge // actual battery power

time, _ := CheckTimeRange(start_hr, end_hr)

if !time { // out of the time range => do not charge
switch {
case !time: // out of the time range => do not charge
u.Log.Infof("Out time range start_time: %s - end_time: %s", start_hr, end_hr)
Setdefaults(fronius_ip, p)
} else if pw_batt2charge == 0 { // battery 100% => do not charge
u.Log.Infof("Battery to charge: %f %%", pw_batt2charge)
case pw_batt2charge == 0: // battery 100% => do not charge
u.Log.Info("Battery is full charged")
Setdefaults(fronius_ip, p)
} else { // in the time range
pw_pv_net := pw_forecast - pw_consumption
if pw_pv_net <= 0 { // net pv power is not enough
u.Log.Infof("Net Forecast Power is not enough: %f W", pw_pv_net)
pw_batt := float64(pw_batt_max) - pw_batt2charge
pw_grid = pw_batt + pw_pv_net
if pw_grid < 0 { // Battery Capacity is not enough => charge the diff
u.Log.Infof("Battery Capacity is not enough: %f W", pw_batt)
ch_pc = SetChargePower(float64(pw_batt_max), -1*pw_grid, float64(max_charge))
ForceCharge(fronius_ip, ch_pc, p)
} else { // Battery Capacity is enough => do not charge
u.Log.Infof("Battery Capacity is enough: %f W", pw_batt)
Setdefaults(fronius_ip, p)
}
} else { // net pv power is enough => do not charge
u.Log.Infof("Net Forecast Power is enough: %f W", pw_pv_net)
case pw_batt < pw_batt_reserve: // battery is less than reserve => charge
ch_pc = SetChargePower(pw_batt_max, pw_batt_reserve, max_charge)
ForceCharge(fronius_ip, ch_pc, p)

default:
pw_grid, charge_enabled := ChargeBattery(pw_pv_net, pw_batt)
if charge_enabled {
ch_pc = SetChargePower(pw_batt_max, -1*pw_grid, max_charge)
ForceCharge(fronius_ip, ch_pc, p)
} else {
Setdefaults(fronius_ip, p)
}

}

return ch_pc, nil
}

Expand All @@ -48,6 +47,25 @@ func SetChargePower(max float64, load float64, limit float64) int16 {

}

func ChargeBattery(pw_pv_net float64, pw_batt float64) (float64, bool) {
enabled := false
pw_grid := pw_batt + pw_pv_net

if pw_pv_net <= 0 { // net pv power is not enough
u.Log.Infof("Net Forecast Power is not enough: %f W", pw_pv_net)
if pw_grid < 0 { // Battery Capacity is not enough => charge the diff
u.Log.Infof("Battery Capacity is not enough: %f W", pw_batt)
enabled = true
} else { // Battery Capacity is enough => do not charge
u.Log.Infof("Battery Capacity is enough: %f W", pw_batt)
}
} else { // net pv power is enough => do not charge
u.Log.Infof("Net Forecast Power is enough: %f W", pw_pv_net)
}

return pw_grid, enabled
}

func CheckTimeRange(start_hr string, end_hr string) (bool, error) {
now := time.Now()

Expand Down

0 comments on commit 935f75f

Please sign in to comment.