Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Prometheus support v2 #342

Open
wants to merge 78 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
5e21653
feat(prometheus): Implement snip runner and http router for Prometheus
kereis Feb 12, 2021
3f7bafd
Ignore JetBrains project files
kereis Feb 18, 2021
12e0f65
feat(prometheus): Remove Prometheusd and add global metrics
kereis Feb 18, 2021
289110d
feat(prometheus): Fix missing parameter when tracking total devices c…
kereis Feb 18, 2021
bc6e5d9
feat(prometheus): Refactor creation of Counter/Gauge options objects
kereis Feb 18, 2021
af449a0
feat(prometheus): Implement approach of dynamically adding counters/g…
kereis Feb 20, 2021
0d48df0
feat(prometheus): Trim trailing underscore for Measurement.Prometheus…
kereis Feb 20, 2021
f67f9f9
feat(prometheus): Delegate metric type storage to measurements.go
kereis Feb 20, 2021
74292b8
feat(prometheus): Fix wrong transcription of kWh and kvarh
kereis Feb 20, 2021
34ef53d
feat(prometheus): Refactor Unit in measurements.go to dedicated enum
kereis Feb 22, 2021
9241b4e
feat(prometheus): Refactor Measurement to use typed unit and promethe…
kereis Feb 22, 2021
561929d
feat(prometheus): Create measurement metrics for device manufacturers
kereis Feb 22, 2021
b870044
feat(prometheus): Add documentation
kereis Feb 25, 2021
c726df3
feat(prometheus): Initialize static metrics
kereis Feb 25, 2021
6abe330
feat(prometheus): Add suggestion notes for more static metrics
kereis Feb 25, 2021
183af85
feat(prometheus): Restructure prometheus_metrics package
kereis Feb 26, 2021
b92073b
feat(prometheus): Add socket metrics and categorize static metrics
kereis Feb 26, 2021
d28df2f
feat(prometheus): Remove metric suggestions for broadcasts
kereis Feb 26, 2021
0e076dd
feat(prometheus): Add metrics for InfluxDB
kereis Feb 26, 2021
820860a
feat(prometheus): Add metrics for Homie
kereis Feb 26, 2021
ca72bd3
feat(prometheus): Change way of registering Prometheus metrics
kereis Feb 26, 2021
9dd4e4a
feat(prometheus): Add metrics for MQTT
kereis Feb 26, 2021
4a898b4
feat(prometheus): Add metrics for devices and SunSpec devices explicitly
kereis Feb 27, 2021
9b3694a
feat(prometheus): Fix excessive label value for devices created metric
kereis Mar 1, 2021
51fdf87
feat(prometheus): Fix small documentation flaws for measurements
kereis Mar 1, 2021
409c55b
feat(prometheus): Add basic tests for measurement definitions
kereis Mar 1, 2021
0b0b973
feat(prometheus): Add full names of units
kereis Mar 4, 2021
998cc57
feat(prometheus): Move subsystem name into label, no panic on register
kereis Mar 4, 2021
88b1ac6
feat(prometheus): Add generalized device info
kereis Mar 6, 2021
fc5f165
feat(prometheus): Add missing help texts for several counters/gauges
kereis Mar 14, 2021
42681e0
feat(prometheus): Update documentation for measurement metrics
kereis Mar 15, 2021
a640465
feat(prometheus): Cleanup
kereis Mar 15, 2021
6d2adbf
feat(prometheus): Integrate device name (by config) for unique metric ID
kereis Mar 15, 2021
ea0d555
feat(prometheus): Move SunSpec labels to name
kereis Mar 19, 2021
8f1b1fb
feat(prometheus): Change help texts and units' full names
kereis Mar 19, 2021
9102271
feat(prometheus): Add conversion funcs for unit
kereis Mar 19, 2021
6ca47ba
feat(prometheus): Don't export internal measurementOptions
kereis Mar 19, 2021
d0f458d
feat(prometheus): Add tests for Prometheus unit and conversion
kereis Mar 19, 2021
70ea74a
feat(prometheus): Change some metrics descriptions
kereis Mar 19, 2021
bef40e5
feat(prometheus): Add device name label to Measurement metrics
kereis Mar 19, 2021
d39849d
refactor(prometheus): Change some measurement names and their Prometh…
kereis Mar 21, 2021
1e541d7
refactor(prometheus): Refactor conversion map to static function
kereis Mar 22, 2021
5339d7d
refactor(prometheus): Refactor unit logic and update measurement desc…
kereis Mar 22, 2021
f740f67
docs(prometheus): Update Prometheus documentation for measurements
kereis Mar 22, 2021
5b308f9
refactor(prometheus): Add Prometheus names for "String N Generation" …
kereis Mar 22, 2021
46cec2e
refactor(prometheus): Cleanup unneeded comments
kereis Mar 22, 2021
c382596
cleanup(prometheus): Remove metrics in scan.go
kereis Apr 19, 2021
f21c7bf
refactor(prometheus): Rename prometheus_metrics package to simply pro…
kereis Apr 19, 2021
61595a7
feat(prometheus): Add custom collector for gauges and counters
kereis Apr 19, 2021
c592480
gitignore add vendor folder
SchumacherFM Jan 7, 2024
ac11b03
go mod update
SchumacherFM Jan 7, 2024
7aafe45
fix rebase errors
SchumacherFM Jan 7, 2024
e28c87b
simplify code in DeviceConfigHandler.createDeviceForManager
SchumacherFM Jan 7, 2024
3c6f515
meter/measurement: add prometheus config
SchumacherFM Jan 7, 2024
6198d5c
prometheus: remove unneeded pointers
SchumacherFM Jan 7, 2024
396233e
meters: remove pointer soup
SchumacherFM Jan 7, 2024
5bab9f4
go mod update to also 1.22.0
SchumacherFM Mar 4, 2024
a58935e
Refactor prometheus integration
SchumacherFM Mar 24, 2024
6fbadb6
Fix websocket path
SchumacherFM Mar 24, 2024
5c5605a
server status add Model name and Serial number
SchumacherFM Mar 24, 2024
f6a3c0f
update readme
SchumacherFM Mar 24, 2024
fbb2de7
gofumpt formatting
SchumacherFM Mar 24, 2024
bf4da45
fix mapping bug in sunspec TmpCab vs TmpSnk, add TmpSnk
SchumacherFM Mar 24, 2024
9d30a4f
prometheus: remove unused code
SchumacherFM Mar 24, 2024
f2956be
promtheus: change the structure of a measurement. move unit to label/val
SchumacherFM Mar 24, 2024
6e2924a
fix tests, add tests
SchumacherFM Mar 26, 2024
9c0691c
sunspec: extend with Status and StatusVendor
SchumacherFM Mar 26, 2024
0cc1374
SunSpec.convertPoint fix panic when parsing non-ScaledValues
SchumacherFM Mar 27, 2024
235ffc5
Docker: ignore vendor, add tmpfs
SchumacherFM Apr 10, 2024
02993b6
Dockerfile: remove make install step
SchumacherFM Apr 10, 2024
27fcc0e
remove arithmetics of conversion kwh to joules
SchumacherFM Apr 10, 2024
9148bc9
index.html: mention sunspec
SchumacherFM Apr 10, 2024
44d0de8
Sunspec.Query: simplify code
SchumacherFM Apr 10, 2024
20d43c3
meters: rename internal funcs
SchumacherFM Apr 14, 2024
4cc888c
MeasurementCounterCollector.Set check if current value is lower
SchumacherFM Jun 2, 2024
77b5c9f
go mod update all
SchumacherFM Aug 3, 2024
dc8ae14
go mod update
SchumacherFM Dec 23, 2024
2d1aa41
fix incorrect websocket path
SchumacherFM Dec 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.vscode
dist/
vendor/
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.vscode/
.idea/
*.json
*.sh
*.yaml
Expand All @@ -7,3 +8,4 @@ linux-*.Dockerfile
mbmd
!entrypoint.sh
!mbmd.dist.yaml
vendor/
9 changes: 4 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,25 @@
############################
FROM golang:alpine as builder

RUN --mount=type=tmpfs,target=/build
WORKDIR /build

# Install git + SSL ca certificates.
# Git is required for fetching the dependencies.
# Ca-certificates is required to call HTTPS endpoints.
RUN apk update && apk add --no-cache git ca-certificates tzdata alpine-sdk && update-ca-certificates

WORKDIR /build

# cache modules
COPY go.mod .
COPY go.sum .
RUN go mod download

COPY . .
RUN make install
RUN make build

#############################
## STEP 2 build a small image
#############################
FROM alpine
FROM alpine:latest

# Import from builder.
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,15 @@ By default, readings are published at `/mbmd/<unique id>/<reading>`. Rate limiti

## InfluxDB support

There is also the option to directly insert the data into an influxdb database by using the command-line options available. InfluxDB 1.8 and 2.0 are currently supported. to enable this, add the `--influx-database` and the `--influx-url` commandline parameter. More advanced configuration is available, to learn more checkout the [mbmd_run.md](docs/mbmd_run.md) documentation
There is also the option to directly insert the data into an influxdb database
by using the command-line options available. InfluxDB 1.8 and 2.0 are currently
supported. to enable this, add the `--influx-database` and the `--influx-url`
commandline parameter. More advanced configuration is available, to learn more
checkout the [mbmd_run.md](docs/mbmd_run.md) documentation

## Prometheus support

There is also the option to pull prometheus data via the `/metrics` endpoint.

# Supported Devices

Expand Down Expand Up @@ -297,7 +305,7 @@ Apart from meters, SunSpec-compatible grid inverters connected over TCP
are supported, too. SunSpec defines a default register layout for accessing
the devices.

Supported inverters include popular devices from SolarEdge (SE3000, SE9000)
Supported inverters include popular devices from SolarEdge (SE3000, SE9000, SE17K, SE10K-RWS)
and SMA (Sunny Boy and Sunny TriPower).

In case of TCP connection, the adapter parameter becomes the hostname and port:
Expand Down
7 changes: 6 additions & 1 deletion assets/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -390,13 +390,17 @@ <h1>Status</h1>
<tr>
<th>Meter</th>
<th>Type</th>
<th>Model</th>
<th>Serial</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr v-for="(m, idx) in sorted(meters)">
<td>${ idx }</td>
<td>${ m.Type }</td>
<td>${ m.Model }</td>
<td>${ m.Serial }</td>
<td>${ m.Status }</td>
</tr>
</tbody>
Expand All @@ -406,7 +410,8 @@ <h1>Status</h1>
<div id="about">
<h1>About MBMD</h1>
<p>MBMD collects measurements from modbus devices.
It works with meters like the Eastron SDM630 as well as grid inverters like SMA Sunny Boy.<br/>
It works with meters like the Eastron SDM630 as well as grid inverters like SMA Sunny Boy
and all other inverters implementing the SUNSPEC standard.<br/>
Please <a href="https://github.com/volkszaehler/mbmd">refer to the documentation
</a> for more information.</p>
<p>This installation runs MBMD version {{.SoftwareVersion}} (compiled with {{.GolangVersion}})</p>
Expand Down
8 changes: 6 additions & 2 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,13 @@ function processMessage(data) {

function connectSocket() {
var ws, loc = window.location;
var protocol = loc.protocol == "https:" ? "wss:" : "ws:"
var protocol = loc.protocol === "https:" ? "wss:" : "ws:"
var path = loc.pathname.replace(/\/$/, "")
if (path === "") {
path = "/"
}

ws = new WebSocket(protocol + "//" + loc.hostname + (loc.port ? ":" + loc.port : "") + loc.pathname.replace(/\/$/, "") + "/ws");
ws = new WebSocket(protocol + "//" + loc.hostname + (loc.port ? ":" + loc.port : "") + path + "ws");

ws.onerror = function(evt) {
ws.close();
Expand Down
59 changes: 34 additions & 25 deletions cmd/confighandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"os"
"regexp"
"sort"
"strconv"
"strings"
"time"
Expand All @@ -15,13 +14,20 @@ import (

// Config describes the entire configuration
type Config struct {
API string
Rate time.Duration
Mqtt MqttConfig
Influx InfluxConfig
Adapters []AdapterConfig
Devices []DeviceConfig
Other map[string]interface{} `mapstructure:",remain"`
API string
Rate time.Duration
Mqtt MqttConfig
Influx InfluxConfig
Adapters []AdapterConfig
Devices []DeviceConfig
Prometheus PrometheusConfig
Other map[string]interface{} `mapstructure:",remain"`
}

type PrometheusConfig struct {
Enable bool // defaults to yes
EnableProcessCollector bool
EnableGoCollector bool
}

// MqttConfig describes the mqtt broker configuration
Expand Down Expand Up @@ -117,35 +123,38 @@ func (conf *DeviceConfigHandler) ConnectionManager(connSpec string, rtu bool, ba
return manager
}

var sunspecTypes = map[string]bool{
"FRONIUS": true,
"KACO": true,
"KOSTAL": true,
"SE": true,
"SMA": true,
"SOLAREDGE": true,
"STECA": true,
"SUNS": true,
"SUNSPEC": true,
}

func (conf *DeviceConfigHandler) createDeviceForManager(
manager *meters.Manager,
name string,
meterType string,
subdevice int,
) meters.Device {
var meter meters.Device
meterType = strings.ToUpper(meterType)

var isSunspec bool
sunspecTypes := []string{"FRONIUS", "KOSTAL", "KACO", "SE", "SMA", "SOLAREDGE", "STECA", "SUNS", "SUNSPEC"}
for _, t := range sunspecTypes {
if t == meterType {
isSunspec = true
break
}
}

sort.SearchStrings(sunspecTypes, meterType)
if isSunspec {
meter = sunspec.NewDevice(meterType, subdevice)
if sunspecTypes[meterType] {
meter = sunspec.NewDevice(name, meterType, subdevice)
} else {
if subdevice > 0 {
log.Fatalf("Invalid subdevice number for device %s: %d", meterType, subdevice)
log.Fatalf("Invalid subdevice number for device '%s' (%s): %d", name, meterType, subdevice)
}

var err error
meter, err = rs485.NewDevice(meterType)
meter, err = rs485.NewDevice(name, meterType)
if err != nil {
log.Fatalf("Error creating device %s: %v.", meterType, err)
log.Fatalf("Error creating device '%s' (%s): %v.", name, meterType, err)
}
}

Expand All @@ -170,7 +179,7 @@ func (conf *DeviceConfigHandler) CreateDevice(devConf DeviceConfig) {
if !ok {
log.Fatalf("Missing adapter configuration for device %v", devConf)
}
meter := conf.createDeviceForManager(manager, devConf.Type, devConf.SubDevice)
meter := conf.createDeviceForManager(manager, devConf.Name, devConf.Type, devConf.SubDevice)

if err := manager.Add(devConf.ID, meter); err != nil {
log.Fatalf("Error adding device %v: %v.", devConf, err)
Expand Down Expand Up @@ -226,7 +235,7 @@ func (conf *DeviceConfigHandler) CreateDeviceFromSpec(deviceDef string, timeout
// have been created of the --rtu flag was specified. We'll not re-check this here.
manager := conf.ConnectionManager(connSpec, false, 0, "", timeout)

meter := conf.createDeviceForManager(manager, meterType, subdevice)
meter := conf.createDeviceForManager(manager, "", meterType, subdevice)
if err := manager.Add(uint8(id), meter); err != nil {
log.Fatalf("Error adding device %s: %v. See -h for help.", meterDef, err)
}
Expand Down
4 changes: 4 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package cmd

// Yeah, it compiles
var _ = Execute
10 changes: 9 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/spf13/pflag"
"github.com/spf13/viper"
latest "github.com/tcnksm/go-latest"

"github.com/volkszaehler/mbmd/prometheus"
"github.com/volkszaehler/mbmd/server"
)

Expand Down Expand Up @@ -223,6 +223,7 @@ func run(cmd *cobra.Command, args []string) {
}
}

promCfg := PrometheusConfig{Enable: true}
if cfgFile != "" {
// config file found
log.Printf("config: using %s", viper.ConfigFileUsed())
Expand All @@ -247,7 +248,14 @@ func run(cmd *cobra.Command, args []string) {
confHandler.CreateDevice(dev)
}
}
promCfg = conf.Prometheus
}
// Prometheus manager - Register all static metrics to default registry
prometheus.RegisterAllMetrics(prometheus.Config{
Enable: promCfg.Enable,
EnableProcessCollector: promCfg.EnableProcessCollector,
EnableGoCollector: promCfg.EnableGoCollector,
})

if countDevices(confHandler.Managers) == 0 {
log.Fatal("config: no devices found - terminating")
Expand Down
8 changes: 5 additions & 3 deletions cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ func scan(cmd *cobra.Command, args []string) {
// create devices
devices := make([]meters.Device, 0)
if _, ok := conn.(*meters.TCP); ok {
suns := sunspec.NewDevice("SUNS")
suns := sunspec.NewDevice("", "SUNS")
devices = append(devices, suns)
} else {
for t := range rs485.Producers {
dev, err := rs485.NewDevice(t)
dev, err := rs485.NewDevice("", t)
if err != nil {
log.Fatal(err)
}
Expand All @@ -109,14 +109,16 @@ SCAN:
if !errors.Is(err, meters.ErrPartiallyOpened) {
continue // devices
}

log.Println(err) // log error but continue
}

mr, err := dev.Probe(client)
if err == nil && v.check(mr.Value) {
deviceDescriptor := dev.Descriptor()
log.Printf("device %d: %s type device found, %s: %.2f\r\n",
deviceID,
dev.Descriptor().Manufacturer,
deviceDescriptor.Manufacturer,
mr.Measurement,
mr.Value,
)
Expand Down
57 changes: 31 additions & 26 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,62 +1,67 @@
module github.com/volkszaehler/mbmd

go 1.21

toolchain go1.21.5
go 1.23

require (
github.com/andig/gosunspec v0.0.0-20231205122018-1daccfa17912
github.com/dmarkham/enumer v1.5.9
github.com/eclipse/paho.mqtt.golang v1.4.3
github.com/andig/gosunspec v0.0.0-20240918203654-860ce51d602b
github.com/dmarkham/enumer v1.5.10
github.com/eclipse/paho.mqtt.golang v1.5.0
github.com/gorilla/handlers v1.5.2
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.1
github.com/grid-x/modbus v0.0.0-20240429072715-02314cc902aa
github.com/influxdata/influxdb-client-go/v2 v2.13.0
github.com/spf13/cobra v1.8.0
github.com/gorilla/websocket v1.5.3
github.com/grid-x/modbus v0.0.0-20241004123532-f6c6fb5201b3
github.com/influxdata/influxdb-client-go/v2 v2.14.0
github.com/prometheus/client_golang v1.20.5
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.18.2
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
)

require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/google/go-github v17.0.0+incompatible // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oapi-codegen/runtime v1.1.1 // indirect
github.com/pascaldekloe/name v1.0.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.20.0 // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/tools v0.27.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Expand Down
Loading
Loading