Skip to content

Commit 11c5222

Browse files
committed
Implement collector and basic CLI
1 parent 6d521ca commit 11c5222

9 files changed

Lines changed: 801 additions & 0 deletions

File tree

.github/workflows/release.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: release
2+
on:
3+
push:
4+
tags:
5+
- 'v*'
6+
7+
jobs:
8+
test:
9+
name: Build and package
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout code
13+
uses: actions/checkout@v2
14+
with:
15+
fetch-depth: 0
16+
- name: Setup Go
17+
uses: actions/setup-go@v2
18+
with:
19+
go-version: '~1.16.3'
20+
- name: Run GoReleaser
21+
uses: goreleaser/goreleaser-action@v2
22+
with:
23+
version: latest
24+
args: release --rm-dist
25+
env:
26+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.goreleaser.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
project_name: vattenfall
2+
builds:
3+
- id: vattenfall
4+
binary: vattenfall
5+
mod_timestamp: '{{ .CommitTimestamp }}'
6+
flags:
7+
- -trimpath
8+
ldflags:
9+
- -s
10+
- -w
11+
- -X main.version={{.Version}} -X main.commit={{.FullCommit}} -X main.date={{.CommitDate}} -X main.repository={{.GitURL}}
12+
goos:
13+
- windows
14+
- darwin
15+
- linux
16+
goarch:
17+
- amd64
18+
- arm64
19+
- arm
20+
goarm:
21+
- 7
22+
archives:
23+
- id: vattenfall
24+
builds:
25+
- vattenfall
26+
wrap_in_directory: true
27+
files:
28+
- LICENSE
29+
- README.md
30+
replacements:
31+
darwin: macOS
32+
format_overrides:
33+
- goos: windows
34+
format: zip

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Vattenfall
2+
3+
This is a very basic Prometheus exporter for the Vattenfall electricity spot
4+
prices in Sweden. It'll export one metric, `energy_price_per_kwh` for each
5+
region:
6+
7+
```
8+
# HELP energy_price_per_kwh Energy price per kWh for a region
9+
# TYPE energy_price_per_kwh gauge
10+
energy_price_per_kwh{country="SE",currency="SEK",region="SN1"} 0.4707
11+
energy_price_per_kwh{country="SE",currency="SEK",region="SN2"} 0.4707
12+
energy_price_per_kwh{country="SE",currency="SEK",region="SN3"} 0.4707
13+
energy_price_per_kwh{country="SE",currency="SEK",region="SN4"} 0.4707
14+
```
15+
16+
Data is cached for 30min in memory to not hammer Vattenfall each time you
17+
scrape the collector (and their API is slow), but still catch any price
18+
adjustments that might occur.
19+
20+
## Usage
21+
22+
```
23+
-output.file string
24+
write metrics to specified file (must have .prom extension)
25+
-output.http string
26+
host:port to listen on for HTTP scrapes
27+
-region value
28+
region to query for, SN1-4, can be passed multiple times
29+
```
30+
31+
To run it as a Prometheus exporter that you can query over HTTP:
32+
33+
```sh
34+
$ vattenfall -output.http=":9000" -region SN1 -region SN2 -region SN3 -region SN4
35+
```
36+
37+
Please note that there's 2 endpoints `/metrics` which instruments the
38+
collector itself, and `/prices` with the pricing info.
39+
40+
If you want to use it with the textfile collector, for example in an hourly cron:
41+
42+
```sh
43+
$ vattenfall -output.file="/etc/prometheus/textfile/electricity.prom" -region SN1 -region SN2 -region SN3 -region SN4
44+
```
45+
46+
Or to just get the values on the console:
47+
48+
```sh
49+
$ vattenfall -region SN1 -region SN2 -region SN3 -region SN4
50+
```

data.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"io/ioutil"
8+
"net/http"
9+
"sync"
10+
"time"
11+
)
12+
13+
const (
14+
source = "https://www.vattenfall.se/api/price/spot/pricearea/%s/%s/%s"
15+
)
16+
17+
type Data struct {
18+
Timestamp time.Time
19+
Region string
20+
Value float64
21+
Currency string
22+
}
23+
24+
var (
25+
info = map[string][]Data{}
26+
lastFetch = map[string]time.Time{}
27+
lock = sync.RWMutex{}
28+
)
29+
30+
func (d *Data) UnmarshalJSON(data []byte) error {
31+
type internal struct {
32+
Timestamp string `json:"TimeStamp"`
33+
Value float64 `json:"Value"`
34+
PriceArea string `json:"PriceArea"`
35+
}
36+
37+
var v internal
38+
if err := json.Unmarshal(data, &v); err != nil {
39+
return err
40+
}
41+
42+
parsed, err := time.Parse("2006-01-02T15:04:05", v.Timestamp)
43+
if err != nil {
44+
return err
45+
}
46+
47+
d.Timestamp = parsed
48+
d.Region = v.PriceArea
49+
d.Value = (v.Value * 100) / 10000
50+
d.Currency = "SEK"
51+
52+
return nil
53+
}
54+
55+
func fetch(date time.Time, region string) ([]Data, error) {
56+
lock.RLock()
57+
lf := lastFetch[region]
58+
lock.RUnlock()
59+
60+
if lf.Add(30 * time.Minute).Before(date) {
61+
b, err := fetchFromURL(date, region)
62+
if err != nil {
63+
return nil, err
64+
}
65+
66+
data := []Data{}
67+
err = json.Unmarshal(b, &data)
68+
if err != nil {
69+
return nil, err
70+
}
71+
72+
lock.Lock()
73+
lastFetch[region] = date
74+
info[region] = data
75+
lock.Unlock()
76+
77+
return data, nil
78+
}
79+
80+
return info[region], nil
81+
}
82+
83+
func fetchFromURL(date time.Time, region string) ([]byte, error) {
84+
res := fmt.Sprintf(
85+
source,
86+
date.Format("2006-01-02"),
87+
date.Format("2006-01-02"),
88+
region,
89+
)
90+
resp, err := http.Get(res)
91+
if err != nil {
92+
return nil, fmt.Errorf("failed to fetch %s: %w", res, err)
93+
}
94+
defer func() {
95+
io.Copy(ioutil.Discard, resp.Body)
96+
resp.Body.Close()
97+
}()
98+
99+
if resp.StatusCode != http.StatusOK {
100+
return nil, fmt.Errorf("expected 200 OK when fetching: %s, got: %d", res, resp.StatusCode)
101+
}
102+
103+
data, err := io.ReadAll(resp.Body)
104+
if err != nil {
105+
return nil, fmt.Errorf("failed to read response body: %w", err)
106+
}
107+
108+
return data, nil
109+
}

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module github.com/daenney/vattenfall
2+
3+
go 1.16
4+
5+
require (
6+
github.com/prometheus/client_golang v1.10.0
7+
github.com/prometheus/common v0.18.0
8+
)

0 commit comments

Comments
 (0)