Ocpp: add support for configurable ChargingRateUnit (#10048)

This commit is contained in:
Thomas Vandahl 2023-09-29 16:25:13 +02:00 • committed by GitHub
parent fc0bcf95bb
commit 100e7a559a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 19 deletions

View file

@ -11,6 +11,7 @@ import (
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/charger/ocpp"
"github.com/evcc-io/evcc/core/loadpoint"
"github.com/evcc-io/evcc/util"
"github.com/lorenzodonini/ocpp-go/ocpp1.6/core"
"github.com/lorenzodonini/ocpp-go/ocpp1.6/smartcharging"
@ -30,6 +31,8 @@ type OCPP struct {
meterValuesSample string
timeout time.Duration
phaseSwitching bool
chargingRateUnit types.ChargingRateUnitType
lp loadpoint.API
}
const defaultIdTag = "evcc"
@ -50,11 +53,13 @@ func NewOCPPFromConfig(other map[string]interface{}) (api.Charger, error) {
Timeout time.Duration
BootNotification *bool
GetConfiguration *bool
ChargingRateUnit string
}{
Connector: 1,
IdTag: defaultIdTag,
ConnectTimeout: ocppConnectTimeout,
Timeout: ocppTimeout,
Connector: 1,
IdTag: defaultIdTag,
ConnectTimeout: ocppConnectTimeout,
Timeout: ocppTimeout,
ChargingRateUnit: "A",
}
if err := util.DecodeOther(other, &cc); err != nil {
@ -67,7 +72,7 @@ func NewOCPPFromConfig(other map[string]interface{}) (api.Charger, error) {
c, err := NewOCPP(cc.StationId, cc.Connector, cc.IdTag,
cc.MeterValues, cc.MeterInterval,
boot, noConfig,
cc.ConnectTimeout, cc.Timeout)
cc.ConnectTimeout, cc.Timeout, cc.ChargingRateUnit)
if err != nil {
return c, err
}
@ -104,6 +109,7 @@ func NewOCPP(id string, connector int, idtag string,
meterValues string, meterInterval time.Duration,
boot, noConfig bool,
connectTimeout, timeout time.Duration,
chargingRateUnit string,
) (*OCPP, error) {
unit := "ocpp"
if id != "" {
@ -147,9 +153,12 @@ func NewOCPP(id string, connector int, idtag string,
ocpp.KeyMeterValuesSampledData,
ocpp.KeyMeterValueSampleInterval,
ocpp.KeyConnectorSwitch3to1PhaseSupported,
ocpp.KeyChargingScheduleAllowedChargingRateUnit,
}
_ = keys
c.chargingRateUnit = types.ChargingRateUnitType(chargingRateUnit)
// noConfig mode disables GetConfiguration
if noConfig {
c.meterValuesSample = meterValues
@ -205,6 +214,11 @@ func NewOCPP(id string, connector int, idtag string,
c.idtag = *opt.Value
c.log.DEBUG.Printf("overriding default `idTag` with Alfen-specific value: %s", c.idtag)
}
case ocpp.KeyChargingScheduleAllowedChargingRateUnit:
if *opt.Value == "W" {
c.chargingRateUnit = types.ChargingRateUnitWatts
}
}
if err != nil {
@ -319,7 +333,7 @@ func (c *OCPP) Enable(enable bool) (err error) {
rc <- err
}, c.idtag, func(request *core.RemoteStartTransactionRequest) {
request.ConnectorId = &c.connector
request.ChargingProfile = getTxChargingProfile(c.current, c.phases, 0)
request.ChargingProfile = c.getTxChargingProfile(c.current, 0)
})
} else {
// if no transaction is running, the vehicle may have stopped it (which is ok) or an unknown transaction is running
@ -361,8 +375,8 @@ func (c *OCPP) setChargingProfile(connectorId int, profile *types.ChargingProfil
return c.wait(err, rc)
}
// updatePeriod sets a single charging schedule period with given current and phases
func (c *OCPP) updatePeriod(current float64, phases int) error {
// updatePeriod sets a single charging schedule period with given current
func (c *OCPP) updatePeriod(current float64) error {
// current period can only be updated if transaction is active
if enabled, err := c.Enabled(); err != nil || !enabled {
return err
@ -375,7 +389,7 @@ func (c *OCPP) updatePeriod(current float64, phases int) error {
current = math.Trunc(10*current) / 10
err = c.setChargingProfile(c.connector, getTxChargingProfile(current, phases, txn))
err = c.setChargingProfile(c.connector, c.getTxChargingProfile(current, txn))
if err != nil {
err = fmt.Errorf("set charging profile: %w", err)
}
@ -383,13 +397,19 @@ func (c *OCPP) updatePeriod(current float64, phases int) error {
return err
}
func getTxChargingProfile(current float64, phases, transactionId int) *types.ChargingProfile {
func (c *OCPP) getTxChargingProfile(current float64, transactionId int) *types.ChargingProfile {
period := types.NewChargingSchedulePeriod(0, current)
// TODO add phases support
// if phases != 0 {
// period.NumberPhases = &phases
// }
if c.chargingRateUnit == types.ChargingRateUnitWatts {
var phases int
// get (expectedly) active phases from loadpoint
if c.lp != nil {
phases = c.lp.GetPhases()
}
if phases == 0 {
phases = 3
}
period = types.NewChargingSchedulePeriod(0, math.Trunc(230.0*current*float64(phases)))
}
return &types.ChargingProfile{
ChargingProfileId: 1,
@ -398,7 +418,7 @@ func getTxChargingProfile(current float64, phases, transactionId int) *types.Cha
ChargingProfilePurpose: types.ChargingProfilePurposeTxProfile,
ChargingProfileKind: types.ChargingProfileKindRelative,
ChargingSchedule: &types.ChargingSchedule{
ChargingRateUnit: types.ChargingRateUnitAmperes,
ChargingRateUnit: c.chargingRateUnit,
ChargingSchedulePeriod: []types.ChargingSchedulePeriod{period},
},
}
@ -413,7 +433,7 @@ var _ api.ChargerEx = (*OCPP)(nil)
// MaxCurrentMillis implements the api.ChargerEx interface
func (c *OCPP) MaxCurrentMillis(current float64) error {
err := c.updatePeriod(current, c.phases)
err := c.updatePeriod(current)
if err == nil {
c.current = current
}
@ -442,7 +462,7 @@ func (c *OCPP) phases1p3p(phases int) error {
// NOTE: this will currently _never_ do anything since
// loadpoint disabled the charger before switching so
// updatePeriod will short-circuit
return c.updatePeriod(c.current, c.phases)
return c.updatePeriod(c.current)
}
// // Identify implements the api.Identifier interface
@ -450,3 +470,8 @@ func (c *OCPP) phases1p3p(phases int) error {
// func (c *OCPP) Identify() (string, error) {
// return "", errors.New("not implemented")
// }
// LoadpointControl implements loadpoint.Controller
func (c *OCPP) LoadpointControl(lp loadpoint.API) {
c.lp = lp
}

View file

@ -98,7 +98,7 @@ func (suite *ocppTestSuite) TestConnect() {
suite.True(cp.IsConnected())
// start cp server
c, err := NewOCPP("test", ocppTestConnector, "", "", 0, false, false, ocppTestConnectTimeout, ocppTestTimeout)
c, err := NewOCPP("test", ocppTestConnector, "", "", 0, false, false, ocppTestConnectTimeout, ocppTestTimeout, "A")
if err != nil {
suite.NoError(err)
return

View file

@ -70,6 +70,12 @@ params:
description:
de: Liste der Zählerwerte
en: List of meter values
- name: chargingrateunit
advanced: true
type: string
description:
de: Einheit, in der ChargingProfile-Werte gesetzt werden ("W" oder "A")
en: Unit for setting ChargingProfile values ("W" or "A")
render: |
{{ include "ocpp" . }}
{{- if ne .getconfiguration "true" }}
@ -84,3 +90,6 @@ render: |
{{- if .metervalues }}
metervalues: {{ .metervalues }}
{{- end }}
{{- if .chargingrateunit }}
chargingrateunit: {{ .chargingrateunit }}
{{- end }}