Warp: add currents for Warp2 (#2141)

This commit is contained in:
andig 2021-12-29 16:44:12 +01:00 • committed by GitHub
parent f07b32da83
commit a05b524b84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 121 additions and 100 deletions

View file

@ -2,23 +2,36 @@ package charger
import (
"encoding/json"
"errors"
"fmt"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/charger/warp"
"github.com/evcc-io/evcc/provider"
"github.com/evcc-io/evcc/provider/mqtt"
"github.com/evcc-io/evcc/util"
)
// Warp configures generic charger and charge meter for an Warp loadpoint
type Warp struct {
log *util.Logger
root string
client *mqtt.Client
enabledG func() (string, error)
statusG func() (string, error)
meterG func() (string, error)
meterDetailsG func() (string, error)
enableS func(bool) error
maxcurrentS func(int64) error
enabled bool // cache
}
func init() {
registry.Add("warp", NewWarpFromConfig)
}
const (
warpRootTopic = "warp"
warpTimeout = 30 * time.Second
)
//go:generate go run ../cmd/tools/decorate.go -f decorateWarp -b *Warp -r api.Charger -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)"
// NewWarpFromConfig creates a new configurable charger
func NewWarpFromConfig(other map[string]interface{}) (api.Charger, error) {
@ -26,45 +39,47 @@ func NewWarpFromConfig(other map[string]interface{}) (api.Charger, error) {
mqtt.Config `mapstructure:",squash"`
Topic string
Timeout time.Duration
UseMeter bool
UseMeter interface{}
}{
Topic: warpRootTopic,
Timeout: warpTimeout,
Topic: warp.RootTopic,
Timeout: warp.Timeout,
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
m, err := NewWarp(cc.Config, cc.Topic, cc.Timeout)
wb, err := NewWarp(cc.Config, cc.Topic, cc.Timeout)
if err != nil {
return nil, err
}
var currentPower func() (float64, error)
var totalEnergy func() (float64, error)
if cc.UseMeter {
currentPower = m.currentPower
totalEnergy = m.totalEnergy
if cc.UseMeter != nil {
// TODO remove
util.NewLogger("warp").WARN.Println("usemeter is deprecated and will be removed in a future release")
}
return decorateWarp(m, currentPower, totalEnergy), err
}
detectPro := provider.NewMqtt(wb.log, wb.client,
fmt.Sprintf("%s/evse/low_level_state", wb.root), 1, cc.Timeout,
).StringGetter()
// Warp configures generic charger and charge meter for an Warp loadpoint
type Warp struct {
log *util.Logger
root string
client *mqtt.Client
enabledG func() (string, error)
statusG func() (string, error)
meterG func() (string, error)
enableS func(bool) error
maxcurrentS func(int64) error
enabled bool // cache
}
var isPro bool
if state, err := detectPro(); err == nil {
var res warp.LowLevelState
if err := json.Unmarshal([]byte(state), &res); err != nil {
return nil, err
}
//go:generate go run ../cmd/tools/decorate.go -f decorateWarp -b *Warp -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)"
isPro = len(res.AdcValues) > 2
}
var currents func() (float64, float64, float64, error)
if isPro {
currents = wb.currents
}
return decorateWarp(wb, currents), err
}
// NewWarp creates a new configurable charger
func NewWarp(mqttconf mqtt.Config, topic string, timeout time.Duration) (*Warp, error) {
@ -99,6 +114,7 @@ func NewWarp(mqttconf mqtt.Config, topic string, timeout time.Duration) (*Warp,
m.enabledG = stringG(fmt.Sprintf("%s/evse/auto_start_charging", topic))
m.statusG = stringG(fmt.Sprintf("%s/evse/state", topic))
m.meterG = stringG(fmt.Sprintf("%s/meter/state", topic))
m.meterDetailsG = stringG(fmt.Sprintf("%s/meter/detailed_values", topic))
m.enableS = provider.NewMqtt(log, client,
fmt.Sprintf("%s/evse/auto_start_charging_update", topic), 1, 0).
@ -113,19 +129,6 @@ func NewWarp(mqttconf mqtt.Config, topic string, timeout time.Duration) (*Warp,
return m, nil
}
type warpStatus struct {
Iec61851State int `json:"iec61851_state"`
VehicleState int `json:"vehicle_state"`
ChargeRelease int `json:"charge_release"`
ContactorState int `json:"contactor_state"`
ContactorError int `json:"contactor_error"`
AllowedChargingCurrent int64 `json:"allowed_charging_current"`
ErrorState int `json:"error_state"`
LockState int `json:"lock_state"`
TimeSinceStateChange int64 `json:"time_since_state_change"`
Uptime int64 `json:"uptime"`
}
// Enable implements the api.Charger interface
func (m *Warp) Enable(enable bool) error {
// set auto_start_charging
@ -149,8 +152,8 @@ func (m *Warp) Enable(enable bool) error {
return err
}
func (m *Warp) status() (warpStatus, error) {
var res warpStatus
func (m *Warp) status() (warp.Status, error) {
var res warp.Status
s, err := m.statusG()
if err == nil {
@ -179,7 +182,7 @@ func (m *Warp) autostart() (bool, error) {
func (m *Warp) isEnabled() (bool, error) {
enabled, err := m.autostart()
var status warpStatus
var status warp.Status
if err == nil {
status, err = m.status()
}
@ -221,7 +224,7 @@ func (m *Warp) Enabled() (bool, error) {
// Status implements the api.Charger interface
func (m *Warp) Status() (api.ChargeStatus, error) {
var status warpStatus
var status warp.Status
s, err := m.statusG()
if err == nil {
@ -257,15 +260,11 @@ func (m *Warp) MaxCurrentMillis(current float64) error {
return m.maxcurrentS(int64(1000 * current))
}
type powerStatus struct {
Power float64 `json:"power"`
EnergyRel float64 `json:"energy_rel"`
EnergyAbs float64 `json:"energy_abs"`
}
var _ api.Meter = (*Warp)(nil)
// currentPower implements the api.Meter interface
func (m *Warp) currentPower() (float64, error) {
var res powerStatus
// CurrentPower implements the api.Meter interface
func (m *Warp) CurrentPower() (float64, error) {
var res warp.PowerStatus
s, err := m.meterG()
if err == nil {
@ -275,9 +274,11 @@ func (m *Warp) currentPower() (float64, error) {
return res.Power, err
}
// totalEnergy implements the api.MeterEnergy interface
func (m *Warp) totalEnergy() (float64, error) {
var res powerStatus
var _ api.MeterEnergy = (*Warp)(nil)
// TotalEnergy implements the api.MeterEnergy interface
func (m *Warp) TotalEnergy() (float64, error) {
var res warp.PowerStatus
s, err := m.meterG()
if err == nil {
@ -286,3 +287,21 @@ func (m *Warp) totalEnergy() (float64, error) {
return res.EnergyAbs, err
}
// currents implements the api.MeterCurrrents interface
func (m *Warp) currents() (float64, float64, float64, error) {
var res []float64
s, err := m.meterDetailsG()
if err == nil {
if err = json.Unmarshal([]byte(s), &res); err == nil {
if len(res) > 5 {
return res[3], res[4], res[5], nil
}
err = errors.New("invalid length")
}
}
return 0, 0, 0, err
}

36
charger/warp/types.go Normal file
View file

@ -0,0 +1,36 @@
package warp
import "time"
const (
RootTopic = "warp"
Timeout = 30 * time.Second
)
type Status struct {
Iec61851State int `json:"iec61851_state"`
VehicleState int `json:"vehicle_state"`
ChargeRelease int `json:"charge_release"`
ContactorState int `json:"contactor_state"`
ContactorError int `json:"contactor_error"`
AllowedChargingCurrent int64 `json:"allowed_charging_current"`
ErrorState int `json:"error_state"`
LockState int `json:"lock_state"`
TimeSinceStateChange int64 `json:"time_since_state_change"`
Uptime int64 `json:"uptime"`
}
type LowLevelState struct {
LedState int `json:"led_state"`
CpPwmDutyCycle int `json:"cp_pwm_duty_cycle"`
AdcValues []int `json:"adc_values"`
Voltages []int
Resistances []int
Gpio []bool
}
type PowerStatus struct {
Power float64 `json:"power"`
EnergyRel float64 `json:"energy_rel"`
EnergyAbs float64 `json:"energy_abs"`
}

View file

@ -6,45 +6,19 @@ import (
"github.com/evcc-io/evcc/api"
)
func decorateWarp(base *Warp, meter func() (float64, error), meterEnergy func() (float64, error)) api.Charger {
func decorateWarp(base *Warp, meterCurrent func() (float64, float64, float64, error)) api.Charger {
switch {
case meter == nil && meterEnergy == nil:
case meterCurrent == nil:
return base
case meter != nil && meterEnergy == nil:
case meterCurrent != nil:
return &struct {
*Warp
api.Meter
api.MeterCurrent
}{
Warp: base,
Meter: &decorateWarpMeterImpl{
meter: meter,
},
}
case meter == nil && meterEnergy != nil:
return &struct {
*Warp
api.MeterEnergy
}{
Warp: base,
MeterEnergy: &decorateWarpMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case meter != nil && meterEnergy != nil:
return &struct {
*Warp
api.Meter
api.MeterEnergy
}{
Warp: base,
Meter: &decorateWarpMeterImpl{
meter: meter,
},
MeterEnergy: &decorateWarpMeterEnergyImpl{
meterEnergy: meterEnergy,
MeterCurrent: &decorateWarpMeterCurrentImpl{
meterCurrent: meterCurrent,
},
}
}
@ -52,18 +26,10 @@ func decorateWarp(base *Warp, meter func() (float64, error), meterEnergy func()
return nil
}
type decorateWarpMeterImpl struct {
meter func() (float64, error)
type decorateWarpMeterCurrentImpl struct {
meterCurrent func() (float64, float64, float64, error)
}
func (impl *decorateWarpMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateWarpMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateWarpMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
func (impl *decorateWarpMeterCurrentImpl) Currents() (float64, float64, float64, error) {
return impl.meterCurrent()
}