diff --git a/core/loadpoint.go b/core/loadpoint.go index 68ffcfbbce..48ec4452bf 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -405,13 +405,21 @@ func (lp *LoadPoint) updateChargerStatus() error { // detectPhases uses MeterCurrent interface to count phases with current >=1A func (lp *LoadPoint) detectPhases() { - if phaseMeter, ok := lp.chargeMeter.(api.MeterCurrent); ok { - i1, i2, i3, err := phaseMeter.Currents() - if err != nil { - lp.log.ERROR.Printf("charge meter error: %v", err) - return - } + phaseMeter, ok := lp.chargeMeter.(api.MeterCurrent) + if !ok { + return + } + i1, i2, i3, err := phaseMeter.Currents() + if err != nil { + lp.log.ERROR.Printf("charge meter error: %v", err) + return + } + + lp.log.TRACE.Printf("charge currents: %vA", []float64{i1, i2, i3}) + lp.publish("chargeCurrents", []float64{i1, i2, i3}) + + if lp.charging { var phases int64 for _, i := range []float64{i1, i2, i3} { if i >= minActiveCurrent { @@ -630,10 +638,8 @@ func (lp *LoadPoint) Update(sitePower float64) { lp.handler.SyncEnabled() } - // phase detection - run only when actually charging - if lp.charging { - lp.detectPhases() - } + // phase detection + lp.detectPhases() // check if car connected and ready for charging var err error diff --git a/core/site.go b/core/site.go index b67cf6ce41..ff703b3772 100644 --- a/core/site.go +++ b/core/site.go @@ -282,6 +282,15 @@ func (site *Site) updateMeters() error { err = retryMeter("battery", site.batteryMeter, &site.batteryPower) } + // currents + if phaseMeter, ok := site.gridMeter.(api.MeterCurrent); err == nil && ok { + i1, i2, i3, err := phaseMeter.Currents() + if err == nil { + site.log.TRACE.Printf("grid currents: %vA", []float64{i1, i2, i3}) + site.publish("gridCurrents", []float64{i1, i2, i3}) + } + } + return err } diff --git a/server/influxdb.go b/server/influxdb.go index 88139a0582..24ca1dfbd6 100644 --- a/server/influxdb.go +++ b/server/influxdb.go @@ -49,6 +49,24 @@ func NewInfluxClient(url, token, org, user, password, database string) *Influx { } } +// supportedType checks if type can be written as influx value +func (m *Influx) supportedType(p util.Param) bool { + if p.Val == nil { + return true + } + + switch val := p.Val.(type) { + case float64: + return true + case [3]float64: + return true + case []float64: + return len(val) == 3 + default: + return false + } +} + // Run Influx publisher func (m *Influx) Run(loadPoints []*core.LoadPoint, in <-chan util.Param) { writer := m.client.WriteApi(m.org, m.database) @@ -62,8 +80,7 @@ func (m *Influx) Run(loadPoints []*core.LoadPoint, in <-chan util.Param) { // add points to batch for async writing for param := range in { - // allow nil value to be written as series gaps - if _, ok := param.Val.(float64); param.Val != nil && !ok { + if !m.supportedType(param) { continue } @@ -72,10 +89,28 @@ func (m *Influx) Run(loadPoints []*core.LoadPoint, in <-chan util.Param) { tags["loadpoint"] = loadPoints[*param.LoadPoint].Name() } - fields := map[string]interface{}{ - "value": param.Val, + fields := map[string]interface{}{} + + // array to slice + val := param.Val + if v, ok := val.([3]float64); ok { + val = v[:] } + // add slice as phase values + if phases, ok := val.([]float64); ok { + var total float64 + for i, v := range phases { + total += v + fields[fmt.Sprintf("l%d", i+1)] = v + } + + // add total as "value" + val = total + } + + fields["value"] = val + // write asynchronously m.log.TRACE.Printf("write %s=%v (%v)", param.Key, param.Val, tags) p := influxdb2.NewPoint(param.Key, tags, fields, time.Now()) diff --git a/server/mqtt.go b/server/mqtt.go index 81c9f265cc..6e875fda92 100644 --- a/server/mqtt.go +++ b/server/mqtt.go @@ -44,11 +44,27 @@ func (m *MQTT) encode(v interface{}) string { return s } -func (m *MQTT) publish(topic string, retained bool, payload interface{}) { +func (m *MQTT) publishSingleValue(topic string, retained bool, payload interface{}) { token := m.Handler.Client.Publish(topic, m.Handler.Qos, retained, m.encode(payload)) go m.Handler.WaitForToken(token) } +func (m *MQTT) publish(topic string, retained bool, payload interface{}) { + if slice, ok := payload.([]float64); ok && len(slice) == 3 { + // publish phase values + var total float64 + for i, v := range slice { + total += v + m.publishSingleValue(fmt.Sprintf("%s/l%d", topic, i+1), retained, v) + } + + // publish sum value + payload = total + } + + m.publishSingleValue(topic, retained, payload) +} + type apiHandler interface { SetMode(api.ChargeMode) SetTargetSoC(int)