diff --git a/charger/alphatec.go b/charger/alphatec.go index 6a49b6177..0f9c66334 100644 --- a/charger/alphatec.go +++ b/charger/alphatec.go @@ -29,7 +29,6 @@ import ( // Alphatec charger implementation type Alphatec struct { - log *util.Logger conn *modbus.Connection } @@ -71,7 +70,6 @@ func NewAlphatec(uri, device, comset string, baudrate int, proto modbus.Protocol conn.Logger(log.TRACE) wb := &Alphatec{ - log: log, conn: conn, } diff --git a/charger/prachtalpha.go b/charger/prachtalpha.go new file mode 100644 index 000000000..3a0dcbc84 --- /dev/null +++ b/charger/prachtalpha.go @@ -0,0 +1,159 @@ +package charger + +// LICENSE + +// Copyright (c) 2019-2022 andig + +// 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. + +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" +) + +// PrachtAlpha charger implementation +type PrachtAlpha struct { + conn *modbus.Connection + vehicle uint16 + curr uint16 +} + +const ( + prachtMaxCurrent = 40004 - 40001 + prachtStatus = 30107 - 30001 +) + +func init() { + registry.Add("pracht-alpha", NewPrachtAlphaFromConfig) +} + +// https://www.prachtenergy.com/wp-content/uploads/2021/03/instruction-manual-charging-station-pracht-alphaXT-eng-1.pdf + +// NewPrachtAlphaFromConfig creates a PrachtAlpha charger from generic config +func NewPrachtAlphaFromConfig(other map[string]interface{}) (api.Charger, error) { + cc := struct { + Vehicle uint16 + modbus.Settings `mapstructure:",squash"` + Timeout time.Duration + }{ + Vehicle: 1, + Settings: modbus.Settings{ + ID: 1, + }, + } + + if err := util.DecodeOther(other, &cc); err != nil { + return nil, err + } + + return NewPrachtAlpha(cc.URI, cc.Device, cc.Comset, cc.Baudrate, modbus.ProtocolFromRTU(cc.RTU), cc.ID, cc.Timeout, cc.Vehicle) +} + +// NewPrachtAlpha creates PrachtAlpha charger +func NewPrachtAlpha(uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8, timeout time.Duration, vehicle uint16) (api.Charger, error) { + conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID) + if err != nil { + return nil, err + } + + if timeout > 0 { + conn.Timeout(timeout) + } + + if !sponsor.IsAuthorized() { + return nil, api.ErrSponsorRequired + } + + log := util.NewLogger("pracht") + conn.Logger(log.TRACE) + + wb := &PrachtAlpha{ + conn: conn, + vehicle: vehicle, + curr: 6, + } + + return wb, err +} + +func (wb *PrachtAlpha) register(reg int) uint16 { + res := uint16(reg) + if wb.vehicle > 1 { + res++ + } + return res +} + +// Status implements the api.Charger interface +func (wb *PrachtAlpha) Status() (api.ChargeStatus, error) { + reg := wb.register(prachtStatus) + b, err := wb.conn.ReadInputRegisters(reg, 1) + if err != nil { + return api.StatusNone, err + } + + switch u := binary.BigEndian.Uint16(b); u { + case 0: + return api.StatusA, nil + case 1: + return api.StatusB, nil + case 2, 3: + return api.StatusC, nil + default: + return api.StatusF, fmt.Errorf("invalid status: %d", u) + } +} + +// Enabled implements the api.Charger interface +func (wb *PrachtAlpha) Enabled() (bool, error) { + reg := wb.register(prachtMaxCurrent) + + b, err := wb.conn.ReadInputRegisters(reg, 1) + if err != nil { + return false, err + } + + return binary.BigEndian.Uint16(b) > 0, nil +} + +// Enable implements the api.Charger interface +func (wb *PrachtAlpha) Enable(enable bool) error { + var curr uint16 + if enable { + curr = wb.curr + } + + return wb.setCurrent(curr) +} + +func (wb *PrachtAlpha) setCurrent(current uint16) error { + reg := wb.register(prachtMaxCurrent) + _, err := wb.conn.WriteSingleRegister(reg, current) + return err +} + +// MaxCurrent implements the api.Charger interface +func (wb *PrachtAlpha) MaxCurrent(current int64) error { + err := wb.setCurrent(uint16(current)) + if err == nil { + wb.curr = uint16(current) + } + return err +} diff --git a/templates/definition/charger/pracht-alpha.yaml b/templates/definition/charger/pracht-alpha.yaml new file mode 100644 index 000000000..c7ca5844e --- /dev/null +++ b/templates/definition/charger/pracht-alpha.yaml @@ -0,0 +1,15 @@ +template: pracht-alpha +products: + - brand: Pracht + description: + generic: Alpha XT +requirements: + evcc: ["sponsorship"] +params: + - name: modbus + choice: ["rs485"] + baudrate: 9600 + comset: 8N1 +render: | + type: pracht-alpha + {{- include "modbus" . }} diff --git a/templates/docs/charger/pracht-alpha_0.yaml b/templates/docs/charger/pracht-alpha_0.yaml new file mode 100644 index 000000000..d42112b51 --- /dev/null +++ b/templates/docs/charger/pracht-alpha_0.yaml @@ -0,0 +1,21 @@ +product: + brand: Pracht + description: Alpha XT +requirements: ["sponsorship"] +render: + - default: | + type: template + template: pracht-alpha + + # RS485 via adapter (Modbus RTU) + modbus: rs485serial + id: 1 + device: /dev/ttyUSB0 # USB-RS485 Adapter Adresse + baudrate: 9600 # Prüfe die Geräteeinstellungen, typische Werte sind 9600, 19200, 38400, 57600, 115200 + comset: "8N1" # Kommunikationsparameter für den Adapter + + # RS485 via TCP/IP (Modbus RTU) + modbus: rs485tcpip + id: 1 + host: 192.0.2.2 # Hostname + port: 502 # Port diff --git a/templates/evcc.io/brands.json b/templates/evcc.io/brands.json index 5d0883849..c5273114a 100644 --- a/templates/evcc.io/brands.json +++ b/templates/evcc.io/brands.json @@ -26,6 +26,7 @@ "PC Electric", "Phoenix", "Porsche", + "Pracht", "SENEC", "TechniSat", "TinkerForge",