Skip to content

Commit

Permalink
Merge branch 'master' into feature/site-api
Browse files Browse the repository at this point in the history
  • Loading branch information
naltatis committed Sep 27, 2023
2 parents 63547b9 + c920893 commit 89d5470
Show file tree
Hide file tree
Showing 33 changed files with 354 additions and 134 deletions.
2 changes: 1 addition & 1 deletion assets/js/components/GlobalSettingsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
role="switch"
/>
<div class="form-check-label">
<label for="telemetryEnabled">
<label for="hiddenFeaturesEnabled">
{{ $t("settings.hiddenFeatures.value") }}
</label>
</div>
Expand Down
12 changes: 6 additions & 6 deletions charger/easee.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,12 @@ func (c *Easee) Enable(enable bool) (err error) {
opMode := c.opMode
c.mux.Unlock()

defer func() {
if err == nil {
c.enabled = enable
}
}()

// enable charger once if it's switched off
if enablingRequired {
data := easee.ChargerSettings{
Expand Down Expand Up @@ -412,12 +418,6 @@ func (c *Easee) Enable(enable bool) (err error) {
targetCurrent = 32
}

defer func() {
if err == nil {
c.enabled = enable
}
}()

uri := fmt.Sprintf("%s/chargers/%s/commands/%s", easee.API, c.charger, action)
if _, err := c.postJSONAndWait(uri, nil); err != nil {
return err
Expand Down
12 changes: 9 additions & 3 deletions charger/ocpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,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)
request.ChargingProfile = getTxChargingProfile(c.current, c.phases, 0)
})
} else {
// if no transaction is running, the vehicle may have stopped it (which is ok) or an unknown transaction is running
Expand Down Expand Up @@ -368,17 +368,22 @@ func (c *OCPP) updatePeriod(current float64, phases int) error {
return err
}

txn, err := c.cp.TransactionID()
if err != nil {
return err
}

current = math.Trunc(10*current) / 10

err := c.setChargingProfile(c.connector, getTxChargingProfile(current, phases))
err = c.setChargingProfile(c.connector, getTxChargingProfile(current, phases, txn))
if err != nil {
err = fmt.Errorf("set charging profile: %w", err)
}

return err
}

func getTxChargingProfile(current float64, phases int) *types.ChargingProfile {
func getTxChargingProfile(current float64, phases, transactionId int) *types.ChargingProfile {
period := types.NewChargingSchedulePeriod(0, current)

// TODO add phases support
Expand All @@ -388,6 +393,7 @@ func getTxChargingProfile(current float64, phases int) *types.ChargingProfile {

return &types.ChargingProfile{
ChargingProfileId: 1,
TransactionId: transactionId,
StackLevel: 0,
ChargingProfilePurpose: types.ChargingProfilePurposeTxProfile,
ChargingProfileKind: types.ChargingProfileKindRelative,
Expand Down
20 changes: 20 additions & 0 deletions charger/ocpp/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ocpp

const (
// Core profile keys
KeyNumberOfConnectors = "NumberOfConnectors"

// Meter profile keys
KeyMeterValuesSampledData = "MeterValuesSampledData"
KeyMeterValueSampleInterval = "MeterValueSampleInterval"

// Smart Charging profile keys
KeyChargeProfileMaxStackLevel = "ChargeProfileMaxStackLevel"
KeyChargingScheduleAllowedChargingRateUnit = "ChargingScheduleAllowedChargingRateUnit"
KeyChargingScheduleMaxPeriods = "ChargingScheduleMaxPeriods"
KeyConnectorSwitch3to1PhaseSupported = "ConnectorSwitch3to1PhaseSupported"
KeyMaxChargingProfilesInstalled = "MaxChargingProfilesInstalled"

// Alfen specific keys
KeyAlfenPlugAndChargeIdentifier = "PlugAndChargeIdentifier"
)
19 changes: 0 additions & 19 deletions charger/ocpp/cp.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,6 @@ import (
"github.com/lorenzodonini/ocpp-go/ocpp1.6/types"
)

const (
// Core profile keys
KeyNumberOfConnectors = "NumberOfConnectors"

// Meter profile keys
KeyMeterValuesSampledData = "MeterValuesSampledData"
KeyMeterValueSampleInterval = "MeterValueSampleInterval"

// Smart Charging profile keys
KeyChargeProfileMaxStackLevel = "ChargeProfileMaxStackLevel"
KeyChargingScheduleAllowedChargingRateUnit = "ChargingScheduleAllowedChargingRateUnit"
KeyChargingScheduleMaxPeriods = "ChargingScheduleMaxPeriods"
KeyConnectorSwitch3to1PhaseSupported = "ConnectorSwitch3to1PhaseSupported"
KeyMaxChargingProfilesInstalled = "MaxChargingProfilesInstalled"

// Alfen specific keys
KeyAlfenPlugAndChargeIdentifier = "PlugAndChargeIdentifier"
)

// TODO support multiple connectors
// Since ocpp-go interfaces at charge point level, we need to manage multiple connector separately

Expand Down
74 changes: 39 additions & 35 deletions charger/ocpp/cs.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ type CS struct {
cps map[string]*CP
}

// Register registers a chargepoint with the central system.
// The chargepoint identified by id may already be connected in which case initial connection is triggered.
// Register registers a charge point with the central system.
// The charge point identified by id may already be connected in which case initial connection is triggered.
func (cs *CS) Register(id string, cp *CP) error {
cs.mu.Lock()
defer cs.mu.Unlock()

if _, ok := cs.cps[id]; ok && id == "" {
return errors.New("cannot have >1 chargepoint with empty station id")
return errors.New("cannot have >1 charge point with empty station id")
}

// trigger unknown chargepoint connected
// trigger unknown charge point connected
if unknown, ok := cs.cps[id]; ok && unknown == nil {
cp.connect(true)
}
Expand All @@ -43,63 +43,67 @@ func (cs *CS) errorHandler(errC <-chan error) {
}
}

// chargepointByID returns a configured charge point identified by id.
func (cs *CS) chargepointByID(id string) (*CP, error) {
cp, ok := cs.cps[id]
if !ok {
return nil, fmt.Errorf("unknown charge point: %s", id)
}
if cp == nil {
return nil, fmt.Errorf("charge point not configured: %s", id)
}
return cp, nil
}

// NewChargePoint implements ocpp16.ChargePointConnectionHandler
func (cs *CS) NewChargePoint(chargePoint ocpp16.ChargePointConnection) {
cs.mu.Lock()
defer cs.mu.Unlock()

if cp, err := cs.chargepointByID(chargePoint.ID()); err != nil {
// check for anonymous chargepoint
if cp, err := cs.chargepointByID(""); err == nil {
cs.log.INFO.Printf("chargepoint connected, registering: %s", chargePoint.ID())
// check for configured charge point
cp, ok := cs.cps[chargePoint.ID()]
if ok {
cs.log.DEBUG.Printf("charge point connected: %s", chargePoint.ID())

// update id
cp.RegisterID(chargePoint.ID())
// trigger initial connection if charge point is already setup
if cp != nil {
cp.connect(true)
}

cs.cps[chargePoint.ID()] = cp
delete(cs.cps, "")
return
}

cp.connect(true)
// check for configured anonymous charge point
cp, ok = cs.cps[""]
if ok && cp != nil {
cs.log.INFO.Printf("charge point connected, registering: %s", chargePoint.ID())

return
}
// update id
cp.RegisterID(chargePoint.ID())

cs.log.WARN.Printf("chargepoint connected, unknown: %s", chargePoint.ID())
cs.cps[chargePoint.ID()] = cp
delete(cs.cps, "")

// register unknown chargepoint
// when chargepoint setup is complete, it will eventually be associated with the connected id
cs.cps[chargePoint.ID()] = nil
} else {
cs.log.DEBUG.Printf("chargepoint connected: %s", chargePoint.ID())
cp.connect(true)

// trigger initial connection if chargepoint is already setup
if cp != nil {
cp.connect(true)
}
return
}

cs.log.WARN.Printf("unknown charge point connected: %s", chargePoint.ID())

// register unknown charge point
// when charge point setup is complete, it will eventually be associated with the connected id
cs.cps[chargePoint.ID()] = nil
}

// ChargePointDisconnected implements ocpp16.ChargePointConnectionHandler
func (cs *CS) ChargePointDisconnected(chargePoint ocpp16.ChargePointConnection) {
cs.mu.Lock()
defer cs.mu.Unlock()

cs.log.DEBUG.Printf("charge point disconnected: %s", chargePoint.ID())

if cp, err := cs.chargepointByID(chargePoint.ID()); err != nil {
cs.log.ERROR.Printf("chargepoint disconnected: %v", err)
} else {
cs.log.DEBUG.Printf("chargepoint disconnected: %s", chargePoint.ID())

if cp == nil {
// remove unknown chargepoint
delete(cs.cps, chargePoint.ID())
} else {
cp.connect(false)
}
cp.connect(false)
}
}
3 changes: 3 additions & 0 deletions charger/ocpp/cs_core.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ocpp

import (
"fmt"

"github.com/lorenzodonini/ocpp-go/ocpp1.6/core"
"github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware"
"github.com/lorenzodonini/ocpp-go/ocpp1.6/remotetrigger"
Expand Down Expand Up @@ -73,6 +75,7 @@ func (cs *CS) OnBootNotification(id string, request *core.BootNotificationReques
return nil, err
}

fmt.Println("OnBootNotification id", id)
return cp.BootNotification(request)
}

Expand Down
50 changes: 31 additions & 19 deletions charger/ocpp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,50 +27,52 @@ func TestOcpp(t *testing.T) {
type ocppTestSuite struct {
suite.Suite
clock *clock.Mock
cp ocpp16.ChargePoint
}

func (suite *ocppTestSuite) SetupSuite() {
// setup cs
suite.NotNil(ocpp.Instance())

// setup cp
suite.clock = clock.NewMock()
cp := ocpp16.NewChargePoint("test", nil, nil)
suite.NotNil(ocpp.Instance())
}

func (suite *ocppTestSuite) startChargePoint(id string) ocpp16.ChargePoint {
// set a handler for all callback functions
triggerC := make(chan remotetrigger.MessageTrigger, 1)
handler := &ChargePointHandler{triggerC: triggerC}
handler := &ChargePointHandler{
triggerC: make(chan remotetrigger.MessageTrigger, 1),
}

// create charge point with handler
cp := ocpp16.NewChargePoint(id, nil, nil)
cp.SetCoreHandler(handler)
cp.SetRemoteTriggerHandler(handler)

// let cs handle the trigger messages
go func() {
for msg := range triggerC {
suite.handleTrigger(msg)
for msg := range handler.triggerC {
suite.handleTrigger(cp, msg)
}
}()

suite.cp = cp
return cp
}

func (suite *ocppTestSuite) handleTrigger(msg remotetrigger.MessageTrigger) {
func (suite *ocppTestSuite) handleTrigger(cp ocpp16.ChargePoint, msg remotetrigger.MessageTrigger) {
switch msg {
case core.BootNotificationFeatureName:
if res, err := suite.cp.BootNotification("demo", "evcc"); err != nil {
if res, err := cp.BootNotification("demo", "evcc"); err != nil {
suite.T().Log("BootNotification:", err)
} else {
suite.T().Log("BootNotification:", res)
}

case core.StatusNotificationFeatureName:
if res, err := suite.cp.StatusNotification(ocppTestConnector, core.NoError, core.ChargePointStatusAvailable); err != nil {
if res, err := cp.StatusNotification(ocppTestConnector, core.NoError, core.ChargePointStatusAvailable); err != nil {
suite.T().Log("StatusNotification:", err)
} else {
suite.T().Log("StatusNotification:", res)
}

case core.MeterValuesFeatureName:
if res, err := suite.cp.MeterValues(1, []types.MeterValue{
if res, err := cp.MeterValues(1, []types.MeterValue{
{
Timestamp: types.NewDateTime(suite.clock.Now()),
SampledValue: []types.SampledValue{
Expand All @@ -91,20 +93,21 @@ func (suite *ocppTestSuite) handleTrigger(msg remotetrigger.MessageTrigger) {

func (suite *ocppTestSuite) TestConnect() {
// start cp client
suite.NoError(suite.cp.Start(ocppTestUrl))
suite.True(suite.cp.IsConnected())
cp := suite.startChargePoint("test")
suite.NoError(cp.Start(ocppTestUrl))
suite.True(cp.IsConnected())

// start cp server
c, err := NewOCPP("test", ocppTestConnector, "", "", 0, false, false, ocppTestConnectTimeout, ocppTestTimeout)
suite.NoError(err)

if err != nil {
suite.NoError(err)
return
}

suite.clock.Add(ocppTestTimeout)
c.cp.TestClock(suite.clock)

// status
_, err = c.Status()
suite.NoError(err)

Expand All @@ -117,4 +120,13 @@ func (suite *ocppTestSuite) TestConnect() {
f, err = c.totalEnergy()
suite.NoError(err)
suite.Equal(1.2, f)

// 2nd charge point
cp2 := suite.startChargePoint("test2")
suite.NoError(cp2.Start(ocppTestUrl))
suite.True(cp2.IsConnected())

// error on unconfigured 2nd charge point
_, err = cp2.BootNotification("demo", "evcc")
suite.Error(err)
}
11 changes: 6 additions & 5 deletions charger/phoenix-em-eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ func init() {
// NewPhoenixEMEthFromConfig creates a Phoenix charger from generic config
func NewPhoenixEMEthFromConfig(other map[string]interface{}) (api.Charger, error) {
cc := struct {
URI string
ID uint8
Meter struct {
modbus.TcpSettings `mapstructure:",squash"`
Meter struct {
Power, Energy, Currents bool
}
}{
URI: "192.168.0.8:502", // default
ID: 180, // default
TcpSettings: modbus.TcpSettings{
URI: "192.168.0.8:502", // default
ID: 180, // default
},
}

if err := util.DecodeOther(other, &cc); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion charger/phoenix-ev-eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func (wb *PhoenixEVEth) getPhases(reg uint16) (float64, float64, float64, error)

var res [3]float64
for i := 0; i < 3; i++ {
res[i] = float64(encoding.Int32LswFirst(b[2*i:]))
res[i] = float64(encoding.Int32LswFirst(b[4*i:]))
}

return res[0], res[1], res[2], nil
Expand Down
Loading

0 comments on commit 89d5470

Please sign in to comment.