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

Added power price sources and All In Power #1

Merged
merged 1 commit into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
huawei-modbus*
vonkje*
config.yaml
temp/*
11 changes: 11 additions & 0 deletions config.yaml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ http:
port: 8080

modbus:
run: true # Read metrics from inverters every interval
read-metrics-interval: 15 # Seconds

connections:
Expand All @@ -25,3 +26,13 @@ modbus:
unit-id: 2
power-meter: true
luna2000: true

power-prices:
sources:
all-in-power:

victoria-metrics:
url: http://127.0.0.1:8428
# Basic HTTP authentication
username:
password:
5 changes: 5 additions & 0 deletions docs/modbus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Modbus

## Links
- **Modbus definitions** https://www.photovoltaikforum.com/core/attachment/251184-solar-inverter-modbus-interface-definitions-pdf/
- **Helpful modbus definitions guide** https://community.openhab.org/t/reading-data-from-huawei-inverter-sun-2000-3ktl-10ktl-via-modbus-tcp-and-rtu/87670
6 changes: 6 additions & 0 deletions docs/power-prices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Power prices
Power prices can be obtained from multiple sources. We want to create an enviroment which can accept multiple power price sources.

## Support sources
- [All In Power](https://allinpower.nl)

17 changes: 17 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,33 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA=
github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
Expand All @@ -27,6 +39,8 @@ github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSz
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
Expand All @@ -52,6 +66,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
Expand All @@ -69,6 +84,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
2 changes: 1 addition & 1 deletion grafana/sun2000.json
Original file line number Diff line number Diff line change
Expand Up @@ -831,7 +831,7 @@
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "Efficiency",
"legendFormat": "{{inverter}} Efficiency",
"range": true,
"refId": "A",
"useBackend": false
Expand Down
16 changes: 12 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ import (

"gijs.eu/vonkje/http"
"gijs.eu/vonkje/modbus"
"gijs.eu/vonkje/power_prices"
"gijs.eu/vonkje/packages/victoria_metrics"

"github.com/spf13/viper"
"github.com/sirupsen/logrus"
)

type Config struct {
LogLevel string `mapstructure:"log-level"`
HTTP http.Config `mapstructure:"http"`
Modbus modbus.Config `mapstructure:"modbus"`
LogLevel string `mapstructure:"log-level"`
HTTP http.Config `mapstructure:"http"`
Modbus modbus.Config `mapstructure:"modbus"`
VictoriaMetrics victoria_metrics.Config `mapstructure:"victoria-metrics"`
PowerPrices power_prices.Config `mapstructure:"power-prices"`
}

var (
Expand Down Expand Up @@ -85,12 +89,16 @@ func main() {
if err != nil {
logger.WithError(err).Panic("Failed to create modbus client")
}

go modbusClient.Start()

httpServer := http.New(config.HTTP, errChannel, stopCtx, logger)
go httpServer.Start()

victoriaMetricsClient := victoria_metrics.New(config.VictoriaMetrics)

powerPricesClient := power_prices.New(config.PowerPrices, errChannel, stopCtx, logger, victoriaMetricsClient)
go powerPricesClient.Start()

<-stopCtx.Done()

modbusClient.Close()
Expand Down
6 changes: 6 additions & 0 deletions modbus/modbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type Connection struct {
}

type Config struct {
Run bool `mapstructure:"run"`
ReadMetricsInterval uint `mapstructure:"read-metrics-interval"`
Connections []ConnectionConfig `mapstructure:"connections"`
}
Expand Down Expand Up @@ -99,6 +100,11 @@ func (m *Modbus) Close() {
}

func (m *Modbus) Start() {
if !m.config.Run {
m.logger.Warn("Modbus metrics collector is disabled")
return
}

m.logger.Info("Starting modbus metrics collector")

m.updateMetrics()
Expand Down
24 changes: 24 additions & 0 deletions packages/victoria_metrics/entities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package victoria_metrics

type Config struct {
URL string
Username string
Password string
}

type VictoriaMetricsRequest struct {
Metric map[string]string `json:"metric"`
Values []float64 `json:"values"`
Timestamps []int64 `json:"timestamps"`
}

type VictoriaMetricsQueryResponse struct {
Status string `json:"status"`
Data struct {
ResultType string `json:"resultType"`
Result []struct {
Metric map[string]string `json:"metric"`
Values [][]interface{} `json:"values"`
} `json:"result"`
} `json:"data"`
}
128 changes: 128 additions & 0 deletions packages/victoria_metrics/victoria_metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package victoria_metrics

import (
"fmt"
"time"
"bytes"
"errors"
"net/url"
"strconv"
"net/http"
"io/ioutil"
"encoding/json"
"encoding/base64"
)

type VictoriaMetrics struct {
Config Config
Client *http.Client
}

// New creates a new GoVictoria instance
func New(config Config) *VictoriaMetrics {
return &VictoriaMetrics{
Config: config,
Client: &http.Client{},
}
}

// SendMetrics sends the metrics to VictoriaMetrics
func (g *VictoriaMetrics) SendMetrics(requests []VictoriaMetricsRequest) error {
if len(requests) == 0 {
return errors.New("No requests to send")
}

// Loop through the request and build the body
body := ""
for _, requestBody := range requests {
jsonRequest, err := json.Marshal(requestBody)
if err != nil {
return err
}

body += string(jsonRequest)
}

// Create the request to Victoria Metrics
request, err := http.NewRequest("POST", g.Config.URL+"/api/v1/import", bytes.NewBuffer([]byte(body)))
request.Header.Add("Authorization", "Basic "+BasicAuth(g.Config.Username, g.Config.Password))
request.Header.Add("User-Agent", "Vonkje (github.com/GJSBRT/vonkje)")

// Send the request to Victoria Metrics
response, err := g.Client.Do(request)
if err != nil {
return err
}

// Close the response body
defer response.Body.Close()

// Check if the status code is not 204
if response.StatusCode != http.StatusNoContent {
return errors.New(fmt.Sprintf("Victoria Metrics returned a non-200 status code: %d", response.StatusCode))
}

return nil
}

// QueryTimeRange queries Victoria Metrics for metrics in a time range
func (g *VictoriaMetrics) QueryTimeRange(promql string, startTime time.Time, endTime time.Time, step string) (VictoriaMetricsQueryResponse, error) {
// Check if the start time is before the end time
if startTime.After(endTime) {
return VictoriaMetricsQueryResponse{}, errors.New("Start time must be before end time")
}

// Add the query parameters to the request
params := url.Values{}
params.Add("query", promql)
params.Add("start", strconv.FormatInt(startTime.Unix(), 10))
params.Add("end", strconv.FormatInt(endTime.Unix(), 10))
params.Add("step", step)

url := g.Config.URL + "/api/v1/query_range?" + params.Encode()

// Create the request to Victoria Metrics
request, err := http.NewRequest("GET", url, nil)
if err != nil {
return VictoriaMetricsQueryResponse{}, err
}

// Add the query parameters to the request
request.Header.Add("Authorization", "Basic "+BasicAuth(g.Config.Username, g.Config.Password))
request.Header.Add("User-Agent", "Vonkje (github.com/GJSBRT/vonkje)")

// Send the request to Victoria Metrics
response, err := g.Client.Do(request)
if err != nil {
return VictoriaMetricsQueryResponse{}, err
}

// Close the response body
defer response.Body.Close()

// Check if the status code is not 200
if response.StatusCode != http.StatusOK {
return VictoriaMetricsQueryResponse{}, errors.New(fmt.Sprintf("Victoria Metrics returned a non-200 status code: %d", response.StatusCode))
}

// Read the response body
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return VictoriaMetricsQueryResponse{}, err
}

// Unmarshal the response
var metrics VictoriaMetricsQueryResponse
err = json.Unmarshal([]byte(body), &metrics)
if err != nil {
return VictoriaMetricsQueryResponse{}, err
}

return metrics, nil
}

// BasicAuth returns the base64 encoded string for basic auth
func BasicAuth(username, password string) string {
auth := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(auth))
}
Loading
Loading