Victron: add phase switching (#28408)
This commit is contained in:
parent
4a41ad99d1
commit
420a0e9f9b
3 changed files with 135 additions and 21 deletions
|
|
@ -36,13 +36,15 @@ type Victron struct {
|
|||
}
|
||||
|
||||
type victronRegs struct {
|
||||
Mode uint16
|
||||
Energy uint16
|
||||
Power uint16
|
||||
Status uint16
|
||||
SetCurrent uint16
|
||||
Enabled uint16
|
||||
isGX bool
|
||||
Mode uint16
|
||||
Energy uint16
|
||||
Power uint16
|
||||
Status uint16
|
||||
SetCurrent uint16
|
||||
Enabled uint16
|
||||
PhaseSwitch uint16
|
||||
ThreePhaseEnabled uint16
|
||||
isGX bool
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -57,13 +59,15 @@ var (
|
|||
}
|
||||
|
||||
victronEVCS = victronRegs{
|
||||
Mode: 5009,
|
||||
Energy: 5021,
|
||||
Power: 5014,
|
||||
Status: 5015,
|
||||
SetCurrent: 5016,
|
||||
Enabled: 5010,
|
||||
isGX: false,
|
||||
Mode: 5009,
|
||||
Energy: 5021,
|
||||
Power: 5014,
|
||||
Status: 5015,
|
||||
SetCurrent: 5016,
|
||||
Enabled: 5010,
|
||||
PhaseSwitch: 5055,
|
||||
ThreePhaseEnabled: 5100,
|
||||
isGX: false,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -72,17 +76,19 @@ func init() {
|
|||
registry.AddCtx("victron-evcs", NewVictronEVCSFromConfig)
|
||||
}
|
||||
|
||||
// NewVictronGXFromConfig creates a ABB charger from generic config
|
||||
//go:generate go tool decorate -f decorateVictron -b *Victron -r api.Charger -t api.PhaseSwitcher,api.PhaseGetter
|
||||
|
||||
// NewVictronGXFromConfig creates a Victron charger from generic config
|
||||
func NewVictronGXFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
|
||||
return NewVictronFromConfig(ctx, other, victronGX)
|
||||
}
|
||||
|
||||
// NewVictronEVCSFromConfig creates a ABB charger from generic config
|
||||
// NewVictronEVCSFromConfig creates a Victron charger from generic config
|
||||
func NewVictronEVCSFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
|
||||
return NewVictronFromConfig(ctx, other, victronEVCS)
|
||||
}
|
||||
|
||||
// NewVictronFromConfig creates a ABB charger from generic config
|
||||
// NewVictronFromConfig creates a Victron charger from generic config
|
||||
func NewVictronFromConfig(ctx context.Context, other map[string]any, regs victronRegs) (api.Charger, error) {
|
||||
cc := modbus.TcpSettings{
|
||||
ID: cast.ToUint8(regs.isGX) * 100,
|
||||
|
|
@ -119,7 +125,23 @@ func NewVictron(ctx context.Context, uri string, slaveID uint8, regs victronRegs
|
|||
return nil, errors.New("charger must be in manual mode")
|
||||
}
|
||||
|
||||
return wb, nil
|
||||
// phase switching (EVCS only, requires hardware modification with second contactor)
|
||||
var phasesS func(int) error
|
||||
var phasesG func() (int, error)
|
||||
|
||||
if !regs.isGX {
|
||||
b, err := wb.conn.ReadHoldingRegisters(wb.regs.ThreePhaseEnabled, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if binary.BigEndian.Uint16(b) == 1 {
|
||||
phasesS = wb.phases1p3p
|
||||
phasesG = wb.getPhases
|
||||
}
|
||||
}
|
||||
|
||||
return decorateVictron(wb, phasesS, phasesG), nil
|
||||
}
|
||||
|
||||
// Status implements the api.Charger interface
|
||||
|
|
@ -132,7 +154,7 @@ func (wb *Victron) Status() (api.ChargeStatus, error) {
|
|||
u := binary.BigEndian.Uint16(b)
|
||||
switch u {
|
||||
case 0, 1, 2, 3:
|
||||
return api.ChargeStatusString(string('A' + rune(binary.BigEndian.Uint16(b))))
|
||||
return api.ChargeStatusString(string('A' + rune(u)))
|
||||
case 5, 6, 21:
|
||||
return api.StatusB, nil
|
||||
default:
|
||||
|
|
@ -194,3 +216,28 @@ func (wb *Victron) ChargedEnergy() (float64, error) {
|
|||
|
||||
return float64(binary.BigEndian.Uint16(b)) / 100, nil
|
||||
}
|
||||
|
||||
// phases1p3p implements the api.PhaseSwitcher interface
|
||||
func (wb *Victron) phases1p3p(phases int) error {
|
||||
// register 5055: 1 = single phase, 0 = three phase
|
||||
var u uint16
|
||||
if phases == 1 {
|
||||
u = 1
|
||||
}
|
||||
|
||||
_, err := wb.conn.WriteSingleRegister(wb.regs.PhaseSwitch, u)
|
||||
return err
|
||||
}
|
||||
|
||||
// getPhases implements the api.PhaseGetter interface
|
||||
func (wb *Victron) getPhases() (int, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(wb.regs.PhaseSwitch, 1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if binary.BigEndian.Uint16(b) == 1 {
|
||||
return 1, nil
|
||||
}
|
||||
return 3, nil
|
||||
}
|
||||
|
|
|
|||
58
charger/victron_decorators.go
Normal file
58
charger/victron_decorators.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package charger
|
||||
|
||||
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/evcc-io/evcc/api"
|
||||
)
|
||||
|
||||
func decorateVictron(base *Victron, phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
|
||||
switch {
|
||||
case phaseSwitcher == nil:
|
||||
return base
|
||||
|
||||
case phaseGetter == nil && phaseSwitcher != nil:
|
||||
return &struct {
|
||||
*Victron
|
||||
api.PhaseSwitcher
|
||||
}{
|
||||
Victron: base,
|
||||
PhaseSwitcher: &decorateVictronPhaseSwitcherImpl{
|
||||
phaseSwitcher: phaseSwitcher,
|
||||
},
|
||||
}
|
||||
|
||||
case phaseGetter != nil && phaseSwitcher != nil:
|
||||
return &struct {
|
||||
*Victron
|
||||
api.PhaseGetter
|
||||
api.PhaseSwitcher
|
||||
}{
|
||||
Victron: base,
|
||||
PhaseGetter: &decorateVictronPhaseGetterImpl{
|
||||
phaseGetter: phaseGetter,
|
||||
},
|
||||
PhaseSwitcher: &decorateVictronPhaseSwitcherImpl{
|
||||
phaseSwitcher: phaseSwitcher,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type decorateVictronPhaseGetterImpl struct {
|
||||
phaseGetter func() (int, error)
|
||||
}
|
||||
|
||||
func (impl *decorateVictronPhaseGetterImpl) GetPhases() (int, error) {
|
||||
return impl.phaseGetter()
|
||||
}
|
||||
|
||||
type decorateVictronPhaseSwitcherImpl struct {
|
||||
phaseSwitcher func(int) error
|
||||
}
|
||||
|
||||
func (impl *decorateVictronPhaseSwitcherImpl) Phases1p3p(p0 int) error {
|
||||
return impl.phaseSwitcher(p0)
|
||||
}
|
||||
|
|
@ -3,10 +3,19 @@ products:
|
|||
- brand: Victron
|
||||
description:
|
||||
generic: EV Charging Station
|
||||
capabilities: ["1p3p"]
|
||||
requirements:
|
||||
description:
|
||||
en: Enter the host of the charger (not the GX device) and ensure that the charger is in manual mode.
|
||||
de: Trage den Host der Wallbox (nicht des GX-Geräts) ein und stelle sicher, dass die Wallbox sich im Modus "Manual" befindet.
|
||||
en: |
|
||||
Enter the host of the charger (not the GX device) and ensure that the charger is in manual mode.
|
||||
|
||||
To enable 1P/3P phase switching a hardware modification is required: the 4P contactor must be replaced with 2×2P contactors. The first contactor switches L1+N, the second switches L2+L3 and is controlled by the second relay on the PCB. Register 5100 must be set to 1 to enable phase switching.
|
||||
[More info](https://community.victronenergy.com/t/charging-an-ev-with-excess-solar-switching-between-1p-and-3p/21181)
|
||||
de: |
|
||||
Trage den Host der Wallbox (nicht des GX-Geräts) ein und stelle sicher, dass die Wallbox sich im Modus "Manual" befindet.
|
||||
|
||||
Für die 1P/3P Phasenumschaltung ist eine Hardware-Modifikation erforderlich: Der 4P-Schütz muss durch 2×2P-Schütze ersetzt werden. Der erste Schütz schaltet L1+N, der zweite L2+L3 und wird über das zweite Relais auf der Platine gesteuert. Register 5100 muss auf 1 gesetzt werden, um die Phasenumschaltung zu aktivieren.
|
||||
[Mehr Infos](https://community.victronenergy.com/t/charging-an-ev-with-excess-solar-switching-between-1p-and-3p/21181)
|
||||
params:
|
||||
- name: modbus
|
||||
choice: ["tcpip"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue