Skip to content

Commit 60a0c50

Browse files
committed
Handle multiple series/meters from a single meter
- For some meters, there can be separate consumption and generation series/meters. See #7 - This change adds a tag "name" to the metric if more than one series is returned. - If the name has a format like "1234 - Consumption", the characters after the last space will be used as the name.
1 parent b0be7a8 commit 60a0c50

File tree

2 files changed

+45
-17
lines changed

2 files changed

+45
-17
lines changed

internal/app/metrics.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ func WriteMetrics(records []ElectricUsage, config InfluxConfig) error {
2929
if record.CostInCents != nil {
3030
point.AddField("cost", float64(*record.CostInCents)/minutes)
3131
}
32+
if record.MeterName != nil {
33+
point.AddTag("name", *record.MeterName)
34+
}
3235
points = append(points, point)
3336
}
3437
err := writeApi.WritePoint(context.Background(), points...)

internal/app/parser.go

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"io"
88
"os"
9+
"strings"
910
"time"
1011
)
1112

@@ -15,6 +16,7 @@ type ElectricUsage struct {
1516
EndTime time.Time
1617
WattHours int64
1718
CostInCents *int64
19+
MeterName *string
1820
}
1921

2022
// Response holds the parsed response from the SmartHub poll api.
@@ -35,6 +37,7 @@ type SmartHubData struct {
3537
// SmartHubSeries holds parsed response data from the SmartHub poll api.
3638
// It holds a list of SmartHubPoints.
3739
type SmartHubSeries struct {
40+
Name string `json:"name"`
3841
Data []SmartHubPoint `json:"data"`
3942
}
4043

@@ -94,13 +97,13 @@ func ParseReader(readCloser io.ReadCloser) ([]ElectricUsage, error) {
9497
if !ok {
9598
return nil, errors.New("no ELECTRIC key")
9699
}
97-
var usageSeries, costSeries []SmartHubPoint
100+
var usageData, costData []SmartHubSeries
98101
for _, data := range datas {
99102
switch data.Type {
100103
case "USAGE":
101-
usageSeries = data.Series[0].Data
104+
usageData = data.Series
102105
case "COST":
103-
costSeries = data.Series[0].Data
106+
costData = data.Series
104107
}
105108
}
106109
// this is dumb, but the SmartHub api returns "unix timestamps"
@@ -112,20 +115,42 @@ func ParseReader(readCloser io.ReadCloser) ([]ElectricUsage, error) {
112115
return nil, err
113116
}
114117
_, offset := time.Now().In(zone).Zone()
115-
period := time.UnixMilli(usageSeries[1].UnixMillis).Sub(time.UnixMilli(usageSeries[0].UnixMillis))
116-
records := make([]ElectricUsage, len(usageSeries))
117-
for i := range usageSeries {
118-
usage := usageSeries[i]
119-
// see note above about "unix timestamps"
120-
start := time.UnixMilli(usage.UnixMillis).Add(time.Second * time.Duration(-offset))
121-
records[i].StartTime = start
122-
records[i].EndTime = start.Add(period)
123-
records[i].WattHours = int64(usage.Value * 1000)
124-
}
125-
// note: cost is not returned by all SmartHub implementations. So this is a no-op sometimes.
126-
for i := range costSeries {
127-
cost := int64(costSeries[i].Value * 100)
128-
records[i].CostInCents = &cost
118+
119+
seriesCount := len(usageData)
120+
dataCount := len(usageData[0].Data)
121+
records := make([]ElectricUsage, seriesCount*dataCount)
122+
for i := range usageData {
123+
meterName := parseName(usageData[i].Name, seriesCount)
124+
usageSeries := usageData[i].Data
125+
period := time.UnixMilli(usageSeries[1].UnixMillis).Sub(time.UnixMilli(usageSeries[0].UnixMillis))
126+
for j := range usageSeries {
127+
usage := usageSeries[j]
128+
index := j + (i * dataCount)
129+
// see note above about "unix timestamps"
130+
start := time.UnixMilli(usage.UnixMillis).Add(time.Second * time.Duration(-offset))
131+
records[index].StartTime = start
132+
records[index].EndTime = start.Add(period)
133+
records[index].WattHours = int64(usage.Value * 1000)
134+
records[index].MeterName = meterName
135+
}
136+
// note: cost is not returned by all SmartHub implementations.
137+
if len(costData) > i {
138+
costSeries := costData[i].Data
139+
for j := range costSeries {
140+
cost := int64(costSeries[j].Value * 100)
141+
records[j+(i*dataCount)].CostInCents = &cost
142+
}
143+
}
129144
}
130145
return records, nil
131146
}
147+
148+
// parseName returns the last part of a string after a space. If seriesCount is 1, returns nil.
149+
func parseName(name string, seriesCount int) *string {
150+
var result *string
151+
if seriesCount > 1 {
152+
tokens := strings.Split(name, " ")
153+
result = &tokens[len(tokens)-1]
154+
}
155+
return result
156+
}

0 commit comments

Comments
 (0)