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.
Add Bender CC612/613 charge controller (evcc-io#3103)
Co-authored-by: andig <[email protected]>
- Loading branch information
1 parent
167772d
commit baa5c30
Showing
13 changed files
with
417 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,261 @@ | ||
package charger | ||
|
||
// LICENSE | ||
|
||
// Copyright (c) 2022 premultiply | ||
|
||
// This module is NOT covered by the MIT license. All rights reserved. | ||
|
||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
|
||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
|
||
// Supports all chargers based on Bender CC612/613 controller series | ||
// * The modbus server must be enabled | ||
// * The setting 'Modbus Slave Register Address Set' must NOT be set to 'Phoenix' or 'TQ-DM100'. | ||
// Use the third selection labeled 'Ebee', 'Bender' etc. | ||
|
||
import ( | ||
"encoding/binary" | ||
"fmt" | ||
"time" | ||
|
||
"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" | ||
) | ||
|
||
// BenderCC charger implementation | ||
type BenderCC struct { | ||
conn *modbus.Connection | ||
current uint16 | ||
} | ||
|
||
const ( | ||
// all holding type registers | ||
bendRegChargePointState = 122 // Vehicle (Control Pilot) state | ||
bendRegCurrents = 212 // Currents from primary meter (mA) | ||
bendRegTotalEnergy = 218 // Total Energy from primary meter (Wh) | ||
bendRegActivePower = 220 // Active Power from primary meter (W) | ||
bendRegChargedEnergy = 716 // Sum of charged energy for the current session (Wh) | ||
bendRegChargingDuration = 718 // Duration since beginning of charge (Seconds) | ||
bendRegEVCCID = 741 // ASCII representation of the Hex. Values corresponding to the EVCCID. Bytes 0 to 11. | ||
bendRegHemsCurrentLimit = 1000 // Current limit of the HEMS module (A) | ||
|
||
bendRegFirmware = 100 // Application version number | ||
bendRegOcppCpStatus = 104 // Charge Point status according to the OCPP spec. enumaration | ||
bendRegProtocolVersion = 120 // Ebee Modbus TCP Server Protocol Version number | ||
bendRegChargePointModel = 142 // ChargePoint Model. Bytes 0 to 19. | ||
bendRegSmartVehicleDetected = 740 // Returns 1 if an EV currently connected is a smart vehicle, or 0 if no EV connected or it is not a smart vehicle | ||
) | ||
|
||
func init() { | ||
registry.Add("bender", NewBenderCCFromConfig) | ||
} | ||
|
||
// NewBenderCCFromConfig creates a BenderCC charger from generic config | ||
func NewBenderCCFromConfig(other map[string]interface{}) (api.Charger, error) { | ||
cc := modbus.TcpSettings{ | ||
ID: 255, | ||
} | ||
|
||
if err := util.DecodeOther(other, &cc); err != nil { | ||
return nil, err | ||
} | ||
|
||
return NewBenderCC(cc.URI, cc.ID) | ||
} | ||
|
||
// NewBenderCC creates BenderCC charger | ||
func NewBenderCC(uri string, id uint8) (api.Charger, error) { | ||
conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, id) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if !sponsor.IsAuthorized() { | ||
return nil, api.ErrSponsorRequired | ||
} | ||
|
||
log := util.NewLogger("bender") | ||
conn.Logger(log.TRACE) | ||
|
||
wb := &BenderCC{ | ||
conn: conn, | ||
current: 6, // assume min current | ||
} | ||
|
||
return wb, err | ||
} | ||
|
||
// Status implements the api.Charger interface | ||
func (wb *BenderCC) Status() (api.ChargeStatus, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(bendRegChargePointState, 1) | ||
if err != nil { | ||
return api.StatusNone, err | ||
} | ||
|
||
sb := binary.BigEndian.Uint16(b) | ||
|
||
switch sb { | ||
case 1: | ||
return api.StatusA, nil | ||
case 2: | ||
return api.StatusB, nil | ||
case 3: | ||
return api.StatusC, nil | ||
case 4: | ||
return api.StatusD, nil | ||
case 5: | ||
return api.StatusE, nil | ||
default: | ||
return api.StatusNone, fmt.Errorf("invalid status: %d", sb) | ||
} | ||
} | ||
|
||
// Enabled implements the api.Charger interface | ||
func (wb *BenderCC) Enabled() (bool, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(bendRegHemsCurrentLimit, 1) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
cur := binary.BigEndian.Uint16(b) | ||
|
||
return cur != 0, nil | ||
} | ||
|
||
// Enable implements the api.Charger interface | ||
func (wb *BenderCC) Enable(enable bool) error { | ||
b := make([]byte, 2) | ||
if enable { | ||
binary.BigEndian.PutUint16(b, wb.current) | ||
} | ||
|
||
_, err := wb.conn.WriteMultipleRegisters(bendRegHemsCurrentLimit, 1, b) | ||
|
||
return err | ||
} | ||
|
||
// MaxCurrent implements the api.Charger interface | ||
func (wb *BenderCC) MaxCurrent(current int64) error { | ||
if current < 6 { | ||
return fmt.Errorf("invalid current %d", current) | ||
} | ||
|
||
b := make([]byte, 2) | ||
binary.BigEndian.PutUint16(b, uint16(current)) | ||
|
||
_, err := wb.conn.WriteMultipleRegisters(bendRegHemsCurrentLimit, 1, b) | ||
if err == nil { | ||
wb.current = uint16(current) | ||
} | ||
|
||
return err | ||
} | ||
|
||
var _ api.ChargeTimer = (*BenderCC)(nil) | ||
|
||
// ChargingTime implements the api.ChargeTimer interface | ||
func (wb *BenderCC) ChargingTime() (time.Duration, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(bendRegChargingDuration, 2) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return time.Duration(binary.BigEndian.Uint32(b)) * time.Second, nil | ||
} | ||
|
||
var _ api.Meter = (*BenderCC)(nil) | ||
|
||
// CurrentPower implements the api.Meter interface | ||
func (wb *BenderCC) CurrentPower() (float64, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(bendRegActivePower, 2) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return float64(binary.BigEndian.Uint32(b)), nil | ||
} | ||
|
||
var _ api.ChargeRater = (*BenderCC)(nil) | ||
|
||
// ChargedEnergy implements the api.ChargeRater interface | ||
func (wb *BenderCC) ChargedEnergy() (float64, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(bendRegChargedEnergy, 2) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return float64(binary.BigEndian.Uint32(b)) / 1e3, nil | ||
} | ||
|
||
var _ api.MeterEnergy = (*BenderCC)(nil) | ||
|
||
// TotalEnergy implements the api.MeterEnergy interface | ||
func (wb *BenderCC) TotalEnergy() (float64, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(bendRegTotalEnergy, 2) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return float64(binary.BigEndian.Uint32(b)) / 1e3, nil | ||
} | ||
|
||
var _ api.MeterCurrent = (*BenderCC)(nil) | ||
|
||
// Currents implements the api.MeterCurrent interface | ||
func (wb *BenderCC) Currents() (float64, float64, float64, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(bendRegCurrents, 6) | ||
if err != nil { | ||
return 0, 0, 0, err | ||
} | ||
|
||
var curr [3]float64 | ||
for l := 0; l < 3; l++ { | ||
curr[l] = float64(binary.BigEndian.Uint32(b[4*l:4*(l+1)])) / 1e3 | ||
} | ||
|
||
return curr[0], curr[1], curr[2], nil | ||
} | ||
|
||
var _ api.Identifier = (*BenderCC)(nil) | ||
|
||
// Identify implements the api.Identifier interface | ||
func (wb *BenderCC) Identify() (string, error) { | ||
b, err := wb.conn.ReadHoldingRegisters(bendRegEVCCID, 6) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return string(b), nil | ||
} | ||
|
||
var _ api.Diagnosis = (*BenderCC)(nil) | ||
|
||
// Diagnose implements the api.Diagnosis interface | ||
func (wb *BenderCC) Diagnose() { | ||
if b, err := wb.conn.ReadHoldingRegisters(bendRegChargePointModel, 10); err == nil { | ||
fmt.Printf("\tModel:\t%s\n", b) | ||
} | ||
if b, err := wb.conn.ReadHoldingRegisters(bendRegFirmware, 2); err == nil { | ||
fmt.Printf("\tFirmware:\t%s\n", b) | ||
} | ||
if b, err := wb.conn.ReadHoldingRegisters(bendRegProtocolVersion, 2); err == nil { | ||
fmt.Printf("\tProtocol:\t%s\n", b) | ||
} | ||
if b, err := wb.conn.ReadHoldingRegisters(bendRegOcppCpStatus, 1); err == nil { | ||
fmt.Printf("\tOCPP Status:\t%d\n", binary.BigEndian.Uint16(b)) | ||
} | ||
if b, err := wb.conn.ReadHoldingRegisters(bendRegSmartVehicleDetected, 1); err == nil { | ||
fmt.Printf("\tSmart Vehicle:\t%t\n", binary.BigEndian.Uint16(b) != 0) | ||
} | ||
} |
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,45 @@ | ||
template: bender | ||
products: | ||
- brand: Bender | ||
description: | ||
generic: CC612/613 | ||
- brand: Mennekes | ||
description: | ||
generic: Amedio, Amtron ChargeControl, Professional | ||
- brand: Webasto | ||
description: | ||
generic: Live | ||
- brand: Juice | ||
description: | ||
generic: Charger Me | ||
- brand: TechniSat | ||
description: | ||
generic: Technivolt | ||
- brand: Ebee | ||
description: | ||
generic: Wallbox | ||
- brand: Optec | ||
description: | ||
generic: Mobility One | ||
- brand: Garo | ||
description: | ||
generic: GLB, GLB+, LS4, LS4 compact | ||
- brand: Ensto | ||
description: | ||
generic: Chago Wallbox | ||
- brand: Ubitricity | ||
description: | ||
generic: Heinz | ||
requirements: | ||
description: | ||
de: Der Modbus Server muss aktiviert sein. 'Modbus Slave Register Address Set' darf NICHT auf 'Phoenix' oder 'TQ-DM100' eingestellt sein. Die dritte Auswahlmöglichkeit 'Ebee', 'Bender' etc. ist richtig. | ||
en: The Modbus server must be enabled. The setting 'Modbus Slave Register Address Set' must NOT be set to 'Phoenix' or 'TQ-DM100'. Use the third selection labeled 'Ebee', 'Bender' etc. | ||
params: | ||
- name: host | ||
required: true | ||
example: 192.0.2.2 | ||
- name: port | ||
default: 502 | ||
render: | | ||
type: bender | ||
uri: {{ .host }}:{{ .port }} |
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,11 @@ | ||
product: | ||
brand: Bender | ||
description: CC612/613 | ||
description: | | ||
Der Modbus Server muss aktiviert sein. 'Modbus Slave Register Address Set' darf NICHT auf 'Phoenix' oder 'TQ-DM100' eingestellt sein. Die dritte Auswahlmöglichkeit 'Ebee', 'Bender' etc. ist richtig. | ||
render: | ||
- default: | | ||
type: template | ||
template: bender | ||
host: 192.0.2.2 # IP-Adresse oder Hostname | ||
port: 502 # Port # Optional |
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,11 @@ | ||
product: | ||
brand: Mennekes | ||
description: Amedio, Amtron ChargeControl, Professional | ||
description: | | ||
Der Modbus Server muss aktiviert sein. 'Modbus Slave Register Address Set' darf NICHT auf 'Phoenix' oder 'TQ-DM100' eingestellt sein. Die dritte Auswahlmöglichkeit 'Ebee', 'Bender' etc. ist richtig. | ||
render: | ||
- default: | | ||
type: template | ||
template: bender | ||
host: 192.0.2.2 # IP-Adresse oder Hostname | ||
port: 502 # Port # Optional |
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,11 @@ | ||
product: | ||
brand: Webasto | ||
description: Live | ||
description: | | ||
Der Modbus Server muss aktiviert sein. 'Modbus Slave Register Address Set' darf NICHT auf 'Phoenix' oder 'TQ-DM100' eingestellt sein. Die dritte Auswahlmöglichkeit 'Ebee', 'Bender' etc. ist richtig. | ||
render: | ||
- default: | | ||
type: template | ||
template: bender | ||
host: 192.0.2.2 # IP-Adresse oder Hostname | ||
port: 502 # Port # Optional |
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,11 @@ | ||
product: | ||
brand: Juice | ||
description: Charger Me | ||
description: | | ||
Der Modbus Server muss aktiviert sein. 'Modbus Slave Register Address Set' darf NICHT auf 'Phoenix' oder 'TQ-DM100' eingestellt sein. Die dritte Auswahlmöglichkeit 'Ebee', 'Bender' etc. ist richtig. | ||
render: | ||
- default: | | ||
type: template | ||
template: bender | ||
host: 192.0.2.2 # IP-Adresse oder Hostname | ||
port: 502 # Port # Optional |
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,11 @@ | ||
product: | ||
brand: TechniSat | ||
description: Technivolt | ||
description: | | ||
Der Modbus Server muss aktiviert sein. 'Modbus Slave Register Address Set' darf NICHT auf 'Phoenix' oder 'TQ-DM100' eingestellt sein. Die dritte Auswahlmöglichkeit 'Ebee', 'Bender' etc. ist richtig. | ||
render: | ||
- default: | | ||
type: template | ||
template: bender | ||
host: 192.0.2.2 # IP-Adresse oder Hostname | ||
port: 502 # Port # Optional |
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,11 @@ | ||
product: | ||
brand: Ebee | ||
description: Wallbox | ||
description: | | ||
Der Modbus Server muss aktiviert sein. 'Modbus Slave Register Address Set' darf NICHT auf 'Phoenix' oder 'TQ-DM100' eingestellt sein. Die dritte Auswahlmöglichkeit 'Ebee', 'Bender' etc. ist richtig. | ||
render: | ||
- default: | | ||
type: template | ||
template: bender | ||
host: 192.0.2.2 # IP-Adresse oder Hostname | ||
port: 502 # Port # Optional |
Oops, something went wrong.