Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delta AC Max: Corrected mapping for native Modbus TCP connection #16094

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion assets/js/components/BatterySettingsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export default {
bufferStartSoc: Number,
batteryDischargeControl: Boolean,
battery: { type: Array, default: () => [] },
batteryGridChargeLimit: Number,
batteryGridChargeLimit: { type: Number, default: null },
smartCostType: String,
tariffGrid: Number,
currency: String,
Expand Down
2 changes: 1 addition & 1 deletion assets/js/components/ChargingPlanWarnings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export default {
return this.fmtWh(this.planEnergy * 1e3);
},
costLimitExists: function () {
return this.smartCostLimit !== 0;
return this.smartCostLimit !== null;
},
costLimitText: function () {
if (this.isCo2) {
Expand Down
2 changes: 1 addition & 1 deletion assets/js/components/Energyflow/Energyflow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ export default {
return this.fmtPricePerKWh(this.tariffGrid, this.currency, true);
},
batteryGridChargeLimitFmt() {
if (this.batteryGridChargeLimit === null || this.batteryGridChargeLimit === undefined) {
if (this.batteryGridChargeLimit === null) {
return;
}
if (this.co2Available) {
Expand Down
2 changes: 1 addition & 1 deletion assets/js/components/Loadpoint.vue
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export default {
phaseRemaining: Number,
pvRemaining: Number,
pvAction: String,
smartCostLimit: { type: Number, default: 0 },
smartCostLimit: { type: Number, default: null },
smartCostType: String,
smartCostActive: Boolean,
smartCostNextStart: String,
Expand Down
2 changes: 1 addition & 1 deletion assets/js/components/Site.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export default {
batteryPower: Number,
batterySoc: Number,
batteryDischargeControl: Boolean,
batteryGridChargeLimit: Number,
batteryGridChargeLimit: { type: Number, default: null },
batteryGridChargeActive: Boolean,
batteryMode: String,
battery: Array,
Expand Down
4 changes: 2 additions & 2 deletions assets/js/components/VehicleStatus.vue
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export default {
pvRemainingInterpolated: Number,
smartCostActive: Boolean,
smartCostDisabled: Boolean,
smartCostLimit: Number,
smartCostLimit: { type: Number, default: null },
smartCostNextStart: String,
smartCostType: String,
tariffCo2: Number,
Expand Down Expand Up @@ -422,7 +422,7 @@ export default {
});
},
smartCostVisible() {
return !!this.smartCostLimit;
return this.smartCostLimit !== null;
},
smartCostTooltipContent() {
if (!this.smartCostVisible) {
Expand Down
122 changes: 89 additions & 33 deletions charger/delta.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ import (

// Delta charger implementation
type Delta struct {
log *util.Logger
conn *modbus.Connection
lp loadpoint.API
mu sync.Mutex
curr float64
base uint16
enabled bool
isBasic bool
log *util.Logger
conn *modbus.Connection
lp loadpoint.API
mu sync.Mutex
curr float64
base uint16
enabled bool
statusG func() (api.ChargeStatus, error)
statusReasonG func() (api.Reason, error)
}

const (
Expand All @@ -41,6 +42,7 @@ const (

// EVSE - The following Register tables are defined as repeating blocks for each single EVSE
// Read Input Registers (0x04)
// Register deltaRegEvseChargerState NOT availabe on native modbus TCP - only on converted RTU connections
deltaRegEvseState = 0 // EVSE State - UINT16 0: Unavailable, 1: Available, 2: Occupied, 3: Preparing, 4: Charging, 5: Finishing, 6: Suspended EV, 7: Suspended EVSE, 8: Not ready, 9: Faulted
deltaRegEvseChargerState = 1 // EVSE Charger State* - UINT16 0: Charging process not started (no vehicle connected), 1: Connected, waiting for release (by RFID or local), 2: Charging process starts, 3: Charging, 4: Suspended (paused), 5: Charging process successfully completed (vehicle still plugged in), 6: Charging process completed by user (vehicle still plugged in), 7: Charging ended with error (vehicle still connected)
deltaRegEvseActualOutputVoltage = 3 // EVSE Actual Output Voltage* - FLOAT32 [V]
Expand Down Expand Up @@ -102,9 +104,16 @@ func NewDelta(uri, device, comset string, baudrate int, proto modbus.Protocol, s

wb.base = connector * 1000

// check for basic or smart register set
if _, err := wb.conn.ReadInputRegisters(wb.base+deltaRegEvseChargerState, 1); err != nil {
wb.isBasic = true
// used limited (native modbus TCP) status register
wb.statusG = wb.statusOCPP
wb.statusReasonG = wb.statusReasonOCPP


// check if deltaRegEvseChargerState is available (= RTU)
if _, err := wb.conn.ReadInputRegisters(wb.base+deltaRegEvseChargerState, 1); err == nil {
// use enhanced (RTU logic) status register
wb.statusG = wb.statusDelta
wb.statusReasonG = wb.statusReasonDelta
}

b, err := wb.conn.ReadHoldingRegisters(deltaRegCommunicationTimeoutEnabled, 1)
Expand Down Expand Up @@ -140,7 +149,52 @@ func (wb *Delta) heartbeat(timeout time.Duration) {
}

// Status implements the api.Charger interface
func (wb *Delta) Status() (api.ChargeStatus, error) {
func (wb *Delta) statusDelta() (api.ChargeStatus, error) {
b, err := wb.conn.ReadInputRegisters(wb.base+deltaRegEvseChargerState, 1)
if err != nil {
return api.StatusNone, err
}

// 0: Charging process not started (no vehicle connected)
// 1: Connected, waiting for release (by RFID or local)
// 2: Charging process starts
// 3: Charging
// 4: Suspended (loading paused)
// 5: Charging process successfully completed (vehicle still plugged in)
// 6: Charging process completed by user (vehicle still plugged in)
// 7: Charging ended with error (vehicle still connected)

switch s := encoding.Uint16(b); s {
case 0, 1, 2:
return api.StatusA, nil
case 3:
return api.StatusC, nil
case 4, 5, 6, 7:
return api.StatusB, nil
default:
return api.StatusNone, fmt.Errorf("invalid status: %0x", s)
}
}

// statusReason implements the api.StatusReasoner interface
func (wb *Delta) statusReasonDelta() (api.Reason, error) {
b, err := wb.conn.ReadInputRegisters(wb.base+deltaRegEvseChargerState, 1)
if err != nil {
return api.ReasonUnknown, err
}

switch encoding.Uint16(b) {
case 1:
return api.ReasonWaitingForAuthorization, nil
case 7:
return api.ReasonDisconnectRequired, nil
}

return api.ReasonUnknown, nil
}

// Status implements the api.Charger interface
func (wb *Delta) statusOCPP() (api.ChargeStatus, error) {
b, err := wb.conn.ReadInputRegisters(wb.base+deltaRegEvseState, 1)
if err != nil {
return api.StatusNone, err
Expand All @@ -156,15 +210,11 @@ func (wb *Delta) Status() (api.ChargeStatus, error) {
// 7: Suspended EVSE
// 8: Not ready
// 9: Faulted

switch s := encoding.Uint16(b); s {
case 0, 1, 2:
case 0, 1:
return api.StatusA, nil
case 3:
if wb.isBasic {
return api.StatusA, nil
}
return api.StatusB, nil
case 5, 6, 7, 9:
case 2, 3, 5, 6, 7: // not used correctly by protocol conversion
return api.StatusB, nil
case 4:
return api.StatusC, nil
Expand All @@ -173,27 +223,33 @@ func (wb *Delta) Status() (api.ChargeStatus, error) {
}
}

var _ api.StatusReasoner = (*Delta)(nil)

// statusReason implements the api.StatusReasoner interface
func (wb *Delta) StatusReason() (api.Reason, error) {
if !wb.isBasic {
b, err := wb.conn.ReadInputRegisters(wb.base+deltaRegEvseState, 1)
if err != nil {
return api.ReasonUnknown, err
}
func (wb *Delta) statusReasonOCPP() (api.Reason, error) {
b, err := wb.conn.ReadInputRegisters(wb.base+deltaRegEvseState, 1)
if err != nil {
return api.ReasonUnknown, err
}

switch s := encoding.Uint16(b); s {
case 3:
return api.ReasonWaitingForAuthorization, nil
case 5:
return api.ReasonDisconnectRequired, nil
}
switch encoding.Uint16(b) {
case 3:
return api.ReasonWaitingForAuthorization, nil
case 5, 9:
return api.ReasonDisconnectRequired, nil
}

return api.ReasonUnknown, nil
}

func (wb *Delta) Status() (api.ChargeStatus, error) {
return wb.statusG()
}

var _ api.StatusReasoner = (*Delta)(nil)

func (wb *Delta) StatusReason() (api.Reason, error) {
return wb.statusReasonG()
}

// Enabled implements the api.Charger interface
func (wb *Delta) Enabled() (bool, error) {
b, err := wb.conn.ReadHoldingRegisters(wb.base+deltaRegEvseChargingPowerLimit, 2)
Expand Down
16 changes: 7 additions & 9 deletions charger/ocpp/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,17 @@ func (conn *Connector) SetChargingProfile(profile *types.ChargingProfile) error

// getScheduleLimit queries the current or power limit the charge point is currently set to offer
func (conn *Connector) GetScheduleLimit(duration int) (float64, error) {
var limit float64
schedule, err := Instance().GetCompositeScheduleRequest(conn.cp.ID(), conn.id, duration)
if err != nil {
return 0, err
}

if err == nil {
if schedule != nil && len(schedule.ChargingSchedulePeriod) > 0 {
// return first (current) period limit
limit = schedule.ChargingSchedulePeriod[0].Limit
} else {
err = fmt.Errorf("invalid ChargingSchedule")
}
// return first (current) period limit
if schedule != nil && schedule.ChargingSchedule != nil && len(schedule.ChargingSchedule.ChargingSchedulePeriod) > 0 {
return schedule.ChargingSchedule.ChargingSchedulePeriod[0].Limit, nil
}

return limit, err
return 0, fmt.Errorf("invalid ChargingSchedule")
}

// WatchDog triggers meter values messages if older than timeout.
Expand Down
6 changes: 3 additions & 3 deletions charger/ocpp/cs_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,16 @@ func (cs *CS) SetChargingProfileRequest(id string, connector int, profile *types
return wait(err, rc)
}

func (cs *CS) GetCompositeScheduleRequest(id string, connector int, duration int) (*types.ChargingSchedule, error) {
var schedule *types.ChargingSchedule
func (cs *CS) GetCompositeScheduleRequest(id string, connector int, duration int) (*smartcharging.GetCompositeScheduleConfirmation, error) {
var schedule *smartcharging.GetCompositeScheduleConfirmation
rc := make(chan error, 1)

err := cs.GetCompositeSchedule(id, func(request *smartcharging.GetCompositeScheduleConfirmation, err error) {
if err == nil && request != nil && request.Status != smartcharging.GetCompositeScheduleStatusAccepted {
err = errors.New(string(request.Status))
}

schedule = request.ChargingSchedule
schedule = request

rc <- err
}, connector, duration)
Expand Down
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ require (
github.com/cstockton/go-conv v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/enbility/go-avahi v0.0.0-20240829083637-9ae2ef5f5ed2 // indirect
github.com/enbility/go-avahi v0.0.0-20240909195612-d5de6b280d7a // indirect
github.com/enbility/zeroconf/v2 v2.0.0-20240827101515-f3956627c450 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
Expand Down Expand Up @@ -200,12 +200,12 @@ require (

replace gopkg.in/yaml.v3 => github.com/andig/yaml v0.0.0-20240531135838-1ff5761ab467

replace github.com/grid-x/modbus => github.com/evcc-io/modbus v0.0.0-20240503125516-9fd99fe0e438
replace github.com/grid-x/modbus => github.com/evcc-io/modbus v0.0.0-20240911180928-7b1464a53285

replace github.com/lorenzodonini/ocpp-go => github.com/evcc-io/ocpp-go v0.0.0-20240730071053-d69e53b0fce9

replace github.com/enbility/ship-go => github.com/enbility/ship-go v0.0.0-20240904104254-04d944c4f825
replace github.com/enbility/ship-go => github.com/enbility/ship-go v0.0.0-20240909200111-0d37cebbfc21

replace github.com/enbility/spine-go => github.com/enbility/spine-go v0.0.0-20240907194637-0024041a4f36
replace github.com/enbility/spine-go => github.com/enbility/spine-go v0.0.0-20240911140055-b637b5392906

replace github.com/enbility/eebus-go => github.com/enbility/eebus-go v0.0.0-20240907200357-cd3e59d01934
replace github.com/enbility/eebus-go => github.com/enbility/eebus-go v0.0.0-20240911140240-9cc1fcff7307
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -127,21 +127,21 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/enbility/eebus-go v0.0.0-20240907200357-cd3e59d01934 h1:dqPRBE0JpbLmPfIhvqDMKKFJmCChHbN/zfu9YWwXtJc=
github.com/enbility/eebus-go v0.0.0-20240907200357-cd3e59d01934/go.mod h1:WhvimKG+DVyHRr4H++A+9c3F9AuuA7VJtLFo6m/BwxY=
github.com/enbility/go-avahi v0.0.0-20240829083637-9ae2ef5f5ed2 h1:voP4zFVNvVWxlV22T1EUAdZ0VlqVV1pkFLhoTTPgwbA=
github.com/enbility/go-avahi v0.0.0-20240829083637-9ae2ef5f5ed2/go.mod h1:KJXUEgg/b4XZzS+OFfqnykHREsinuNSL/IzJ+nU43P8=
github.com/enbility/ship-go v0.0.0-20240904104254-04d944c4f825 h1:i/n9UFYf660TyC8Y48P3MDxLaWWKRNW3pFjEOVzPzto=
github.com/enbility/ship-go v0.0.0-20240904104254-04d944c4f825/go.mod h1:dzymc1D7BDZUTLVHVt9JRRkFLlBrlUmRKyLJvAe07Mc=
github.com/enbility/spine-go v0.0.0-20240907194637-0024041a4f36 h1:Y44hzp5uIYSEyBMe/zwFzlXKokgZHQ9cv2NiIF1NkZs=
github.com/enbility/spine-go v0.0.0-20240907194637-0024041a4f36/go.mod h1:BDvhbs+XsWDGYwd8eQOzPXc8w/avVFmWKLlSKV/gx9k=
github.com/enbility/eebus-go v0.0.0-20240911140240-9cc1fcff7307 h1:kbLNFmMwNF8qNSPcTIJoOgmzZMh5hwJUZEeu2Sl/tXk=
github.com/enbility/eebus-go v0.0.0-20240911140240-9cc1fcff7307/go.mod h1:D5d+EQVuqhjGBaJwsxljEOwbzTNw4q6HOO3jiVN77L4=
github.com/enbility/go-avahi v0.0.0-20240909195612-d5de6b280d7a h1:foChWb8lhzqa6lWDRs6COYMdp649YlUirFP8GqoT0JQ=
github.com/enbility/go-avahi v0.0.0-20240909195612-d5de6b280d7a/go.mod h1:H64mhYcAQUGUUnVqMdZQf93kPecH4M79xwH95Lddt3U=
github.com/enbility/ship-go v0.0.0-20240909200111-0d37cebbfc21 h1:ZuOja5wms/Yujch+f1wQAw5ASY8mEFUpqf/j2LFrjzA=
github.com/enbility/ship-go v0.0.0-20240909200111-0d37cebbfc21/go.mod h1:8EaCKa2WOVZ/4SpquvZStPakEarjRXQLNHhC4azvaA0=
github.com/enbility/spine-go v0.0.0-20240911140055-b637b5392906 h1:/jhq32DUM6vj/mUwy0134PP6wtP7Rm8SI5tmixZhE4I=
github.com/enbility/spine-go v0.0.0-20240911140055-b637b5392906/go.mod h1:BDvhbs+XsWDGYwd8eQOzPXc8w/avVFmWKLlSKV/gx9k=
github.com/enbility/zeroconf/v2 v2.0.0-20240827101515-f3956627c450 h1:39tnpfiV5OVfYb9sOqYmoivBzTHyNLWSYIxd9Qng1eg=
github.com/enbility/zeroconf/v2 v2.0.0-20240827101515-f3956627c450/go.mod h1:1sUbJ+VE7yLNyRzGoCMjoDWtdZ+bW4aYBKx2+Rw+9hs=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evcc-io/modbus v0.0.0-20240503125516-9fd99fe0e438 h1:I14MN2UauarS2Lo+prnNXEj31MxJS1mEfLUrEnOUBsw=
github.com/evcc-io/modbus v0.0.0-20240503125516-9fd99fe0e438/go.mod h1:WpbUAyptAAi0VAriSRopZa6uhiJOJCTz7KFvgGtNRXc=
github.com/evcc-io/modbus v0.0.0-20240911180928-7b1464a53285 h1:+zUVVXpybAsLXRY4KuTiMye/v5jCOxKG0heTSTHLW2Y=
github.com/evcc-io/modbus v0.0.0-20240911180928-7b1464a53285/go.mod h1:WpbUAyptAAi0VAriSRopZa6uhiJOJCTz7KFvgGtNRXc=
github.com/evcc-io/ocpp-go v0.0.0-20240730071053-d69e53b0fce9 h1:FLv1vmLnfc8DanI5U1qOTe1Zr0OgZ/tuOvHALtZ2sOI=
github.com/evcc-io/ocpp-go v0.0.0-20240730071053-d69e53b0fce9/go.mod h1:ZynYDWGw6CslG3vyPuucLsy6AyE+h3XXYlr39jhNiQY=
github.com/evcc-io/tesla-proxy-client v0.0.0-20240221194046-4168b3759701 h1:3JplY3KS6KMDVDNAU+3+KWmSWmoHIU34qwuIpW6SiHk=
Expand Down
2 changes: 1 addition & 1 deletion i18n/de.toml
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ greenEnergySub1 = "über evcc geladen"
greenEnergySub2 = "seit Oktober 2022"
greenShare = "Sonnenanteil"
greenShareSub1 = "Strom bereitgestellt durch"
greenShareSub2 = "von PV und Speicher"
greenShareSub2 = "PV und Speicher"
power = "Ladeleistung"
powerSub1 = "{activeClients} von {totalClients} Nutzern"
powerSub2 = "laden ..."
Expand Down
2 changes: 1 addition & 1 deletion provider/modbus_sunspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func NewModbusSunspecFromConfig(other map[string]interface{}) (Provider, error)
modbus.Lock()
defer modbus.Unlock()

conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, modbus.Tcp, cc.ID)
conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID)
if err != nil {
return nil, err
}
Expand Down
2 changes: 0 additions & 2 deletions templates/definition/meter/alpha-ess-smile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,5 @@ render: |
type: holding
decode: uint16
scale: 0.1
{{- if .capacity }}
capacity: {{ .capacity }} # kWh
{{- end }}
{{- end }}
Loading