forked from evcc-io/evcc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: premultiply <ulrich.peters@posteo.de>
- Loading branch information
Showing
3 changed files
with
225 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package charger | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/evcc-io/evcc/api" | ||
"github.com/evcc-io/evcc/util" | ||
"github.com/evcc-io/evcc/util/modbus" | ||
"github.com/evcc-io/evcc/util/sponsor" | ||
) | ||
|
||
const ( | ||
cfosRegStatus = 8092 // Input | ||
cfosRegMaxCurrent = 8093 // Holding | ||
cfosRegEnable = 8094 // Coil | ||
) | ||
|
||
// CfosPowerBrain is an charger implementation for cFos PowerBrain wallboxes. | ||
// It uses Modbus TCP to communicate at modbus client id 1 and power meters at id 2 and 3. | ||
// https://www.cfos-emobility.de/en-gb/cfos-power-brain/modbus-registers.htm | ||
type CfosPowerBrain struct { | ||
conn *modbus.Connection | ||
} | ||
|
||
func init() { | ||
registry.Add("cfos", NewCfosPowerBrainFromConfig) | ||
} | ||
|
||
// NewCfosPowerBrainFromConfig creates a cFos charger from generic config | ||
func NewCfosPowerBrainFromConfig(other map[string]interface{}) (api.Charger, error) { | ||
cc := struct { | ||
URI string | ||
ID uint8 | ||
}{ | ||
ID: 1, | ||
} | ||
|
||
if err := util.DecodeOther(other, &cc); err != nil { | ||
return nil, err | ||
} | ||
|
||
return NewCfosPowerBrain(cc.URI, cc.ID) | ||
} | ||
|
||
// NewCfosPowerBrain creates a cFos charger | ||
func NewCfosPowerBrain(uri string, id uint8) (*CfosPowerBrain, error) { | ||
uri = util.DefaultPort(uri, 4701) | ||
|
||
conn, err := modbus.NewConnection(uri, "", "", 0, modbus.TcpFormat, id) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
log := util.NewLogger("cfos") | ||
conn.Logger(log.TRACE) | ||
|
||
if !sponsor.IsAuthorized() { | ||
return nil, api.ErrSponsorRequired | ||
} | ||
|
||
wb := &CfosPowerBrain{ | ||
conn: conn, | ||
} | ||
|
||
return wb, nil | ||
} | ||
|
||
// Status implements the Charger.Status interface | ||
func (wb *CfosPowerBrain) Status() (api.ChargeStatus, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(cfosRegStatus, 1) | ||
if err != nil { | ||
return api.StatusNone, err | ||
} | ||
|
||
switch b[0] { | ||
case 0: // warten | ||
return api.StatusA, nil | ||
case 1: // Fahrzeug erkannt | ||
return api.StatusB, nil | ||
case 2: // laden | ||
return api.StatusC, nil | ||
case 3: // laden mit Kühlung | ||
return api.StatusD, nil | ||
case 4: // kein Strom | ||
return api.StatusE, nil | ||
case 5: // Fehler | ||
return api.StatusF, nil | ||
default: | ||
return api.StatusNone, errors.New("invalid response") | ||
} | ||
} | ||
|
||
// Enabled implements the Charger.Enabled interface | ||
func (wb *CfosPowerBrain) Enabled() (bool, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(cfosRegEnable, 1) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
return b[0] == 1, nil | ||
} | ||
|
||
// Enable implements the Charger.Enable interface | ||
func (wb *CfosPowerBrain) Enable(enable bool) error { | ||
var u uint16 | ||
if enable { | ||
u = 1 | ||
} | ||
|
||
_, err := wb.conn.WriteSingleCoil(cfosRegEnable, u) | ||
|
||
return err | ||
} | ||
|
||
// MaxCurrent implements the Charger.MaxCurrent interface | ||
func (wb *CfosPowerBrain) MaxCurrent(current int64) error { | ||
return wb.MaxCurrentMillis(float64(current)) | ||
} | ||
|
||
var _ api.ChargerEx = (*CfosPowerBrain)(nil) | ||
|
||
// MaxCurrentMillis implements the api.ChargerEx interface | ||
func (wb *CfosPowerBrain) MaxCurrentMillis(current float64) error { | ||
_, err := wb.conn.WriteSingleRegister(cfosRegMaxCurrent, uint16(current*10)) | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package meter | ||
|
||
import ( | ||
"encoding/binary" | ||
|
||
"github.com/evcc-io/evcc/api" | ||
"github.com/evcc-io/evcc/util" | ||
"github.com/evcc-io/evcc/util/modbus" | ||
) | ||
|
||
const ( | ||
cfosRegEnergy = 8058 // energy reading | ||
cfosRegPower = 8062 // power reading | ||
) | ||
|
||
var cfosRegCurrents = []uint16{8064, 8066, 8068} // current readings | ||
|
||
// CfosPowerBrain is a meter implementation for cFos PowerBrain wallboxes. | ||
// It uses Modbus TCP to communicate at modbus client id 1 and power meters at id 2 and 3. | ||
// https://www.cfos-emobility.de/en-gb/cfos-power-brain/modbus-registers.htm | ||
type CfosPowerBrain struct { | ||
conn *modbus.Connection | ||
} | ||
|
||
func init() { | ||
registry.Add("cfos", NewCfosPowerBrainFromConfig) | ||
} | ||
|
||
// NewCfosPowerBrainFromConfig creates a cFos meter from generic config | ||
func NewCfosPowerBrainFromConfig(other map[string]interface{}) (api.Meter, error) { | ||
cc := struct { | ||
URI string | ||
ID uint8 | ||
}{} | ||
|
||
if err := util.DecodeOther(other, &cc); err != nil { | ||
return nil, err | ||
} | ||
|
||
return NewCfosPowerBrain(cc.URI, cc.ID) | ||
} | ||
|
||
// NewCfosPowerBrain creates a cFos meter | ||
func NewCfosPowerBrain(uri string, id uint8) (*CfosPowerBrain, error) { | ||
conn, err := modbus.NewConnection(uri, "", "", 0, modbus.TcpFormat, id) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
log := util.NewLogger("cfos") | ||
conn.Logger(log.TRACE) | ||
|
||
wb := &CfosPowerBrain{ | ||
conn: conn, | ||
} | ||
|
||
return wb, nil | ||
} | ||
|
||
// CurrentPower implements the api.Meter interface | ||
func (wb *CfosPowerBrain) CurrentPower() (float64, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(cfosRegPower, 2) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return float64(binary.BigEndian.Uint32(b)) / 10, err | ||
} | ||
|
||
var _ api.MeterEnergy = (*CfosPowerBrain)(nil) | ||
|
||
// TotalEnergy implements the api.MeterEnergy interface | ||
func (wb *CfosPowerBrain) TotalEnergy() (float64, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(cfosRegEnergy, 4) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return float64(binary.BigEndian.Uint64(b)) / 1e3, err | ||
} | ||
|
||
var _ api.MeterCurrent = (*CfosPowerBrain)(nil) | ||
|
||
// Currents implements the api.MeterCurrent interface | ||
func (wb *CfosPowerBrain) Currents() (float64, float64, float64, error) { | ||
var currents []float64 | ||
for _, regCurrent := range cfosRegCurrents { | ||
b, err := wb.conn.ReadHoldingRegisters(regCurrent, 2) | ||
if err != nil { | ||
return 0, 0, 0, err | ||
} | ||
|
||
currents = append(currents, float64(binary.BigEndian.Uint32(b))/10) | ||
} | ||
|
||
return currents[0], currents[1], currents[2], nil | ||
} |