Compare commits

..

No commits in common. "main" and "v0.0.3" have entirely different histories.
main ... v0.0.3

11 changed files with 72 additions and 2119 deletions

View file

@ -1,5 +1,5 @@
# Use buildx for multi-architecture support # Use buildx for multi-architecture support
FROM --platform=${BUILDPLATFORM} golang:1.23 AS builder FROM --platform=${BUILDPLATFORM} golang:1.21 AS builder
WORKDIR /app WORKDIR /app
@ -11,9 +11,6 @@ RUN go mod download
COPY . . COPY . .
# Build the application for the specified target architecture # Build the application for the specified target architecture
# https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
# Declare TARGETOS and TARGETARCH in the local scope so they can be used in the build stage.
ARG TARGETOS TARGETARCH
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -o p1-logger . RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -o p1-logger .
# Create a minimal runtime image # Create a minimal runtime image

5
esphome/.gitignore vendored
View file

@ -1,5 +0,0 @@
# Gitignore settings for ESPHome
# This is an example and may include too much for your use-case.
# You can modify this file to suit your needs.
/.esphome/
/secrets.yaml

View file

@ -83,7 +83,6 @@ globals:
type: char[32] type: char[32]
restore_value: yes restore_value: yes
# https://esphome.io/components/sensor/dsmr.html
dsmr: dsmr:
uart_id: uart_dsmr uart_id: uart_dsmr
id: dsmr_instance id: dsmr_instance
@ -177,30 +176,6 @@ sensor:
name: "Long Electricity Failures" name: "Long Electricity Failures"
icon: mdi:alert icon: mdi:alert
id: electricity_long_failures id: electricity_long_failures
electricity_sags_l1:
name: "Voltage sags L1"
icon: mdi:alert
id: electricity_sags_l1
electricity_sags_l2:
name: "Voltage sags L1"
icon: mdi:alert
id: electricity_sags_l2
electricity_sags_l3:
name: "Voltage sags L1"
icon: mdi:alert
id: electricity_sags_l3
electricity_swells_l1:
name: "Voltage swells L1"
icon: mdi:alert
id: electricity_swells_l1
electricity_swells_l2:
name: "Voltage swells L1"
icon: mdi:alert
id: electricity_swells_l2
electricity_swells_l3:
name: "Voltage swells L1"
icon: mdi:alert
id: electricity_swells_l3
voltage_l1: voltage_l1:
name: "Voltage Phase 1" name: "Voltage Phase 1"
id: voltage_l1 id: voltage_l1

17
go.mod
View file

@ -1,22 +1,15 @@
module git.hollander.online/energy/p1-logger module git.hollander.online/energy/p1-logger
go 1.23 go 1.21
require ( require (
github.com/eclipse/paho.mqtt.golang v1.5.0 github.com/eclipse/paho.mqtt.golang v1.4.3
github.com/lib/pq v1.10.9 github.com/lib/pq v1.10.9
github.com/pressly/goose/v3 v3.21.1
) )
require ( require (
github.com/mfridman/interpolate v0.0.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
)
require (
github.com/gorilla/websocket v1.5.3 // indirect
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
golang.org/x/net v0.28.0 // indirect golang.org/x/net v0.8.0 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.1.0 // indirect
) )

60
go.sum
View file

@ -1,56 +1,12 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pressly/goose/v3 v3.21.1 h1:5SSAKKWej8LVVzNLuT6KIvP1eFDuPvxa+B6H0w78buQ=
github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfWh8pHEe+vE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/sqlite v1.29.6 h1:0lOXGrycJPptfHDuohfYgNqoe4hu+gYuN/pKgY5XjS4=
modernc.org/sqlite v1.29.6/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

View file

@ -1,902 +0,0 @@
{
"__inputs": [
{
"name": "DS_P1",
"label": "p1",
"description": "",
"type": "datasource",
"pluginId": "grafana-postgresql-datasource",
"pluginName": "PostgreSQL"
}
],
"__elements": {},
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "11.1.1"
},
{
"type": "datasource",
"id": "grafana-postgresql-datasource",
"name": "PostgreSQL",
"version": "1.0.0"
},
{
"type": "panel",
"id": "timeseries",
"name": "Time series",
"version": ""
}
],
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "watt"
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 12,
"x": 0,
"y": 0
},
"id": 8,
"options": {
"legend": {
"calcs": [
"min",
"max",
"mean",
"stdDev",
"last"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "-- SELECT time_bucket('$__interval', timestamp) AS time, \n-- delivery_l1 - returning_l1 AS \"Vermogen L1\", \n-- delivery_l2 - returning_l2 AS \"Vermogen L2\", \n-- delivery_l3 - returning_l3 AS \"Vermogen L3\",\n-- delivery_all - returning_all AS \"Vermogen totaal\"\n-- FROM p1\n-- WHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz\n\nWITH RECURSIVE DateRange AS (\n SELECT generate_series($__timeFrom()::timestamp, $__timeTo()::timestamp, '$__interval') AS timestamp\n),\nLastValues AS (\n SELECT\n dr.timestamp,\n (SELECT delivery_l1 FROM p1 WHERE timestamp <= dr.timestamp AND delivery_l1 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS delivery_l1,\n (SELECT delivery_l2 FROM p1 WHERE timestamp <= dr.timestamp AND delivery_l2 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS delivery_l2,\n (SELECT delivery_l3 FROM p1 WHERE timestamp <= dr.timestamp AND delivery_l3 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS delivery_l3,\n (SELECT delivery_all FROM p1 WHERE timestamp <= dr.timestamp AND delivery_all IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS delivery_all,\n (SELECT returning_l1 FROM p1 WHERE timestamp <= dr.timestamp AND returning_l1 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS returning_l1,\n (SELECT returning_l2 FROM p1 WHERE timestamp <= dr.timestamp AND returning_l2 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS returning_l2,\n (SELECT returning_l3 FROM p1 WHERE timestamp <= dr.timestamp AND returning_l3 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS returning_l3,\n (SELECT returning_all FROM p1 WHERE timestamp <= dr.timestamp AND returning_all IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS returning_all\n FROM DateRange dr\n)\nSELECT\n timestamp,\n COALESCE(delivery_l1, 0) - COALESCE(returning_l1, 0) AS \"Leistung L1\",\n COALESCE(delivery_l2, 0) - COALESCE(returning_l2, 0) AS \"Leistung L2\",\n COALESCE(delivery_l3, 0) - COALESCE(returning_l3, 0) AS \"Leistung L3\",\n COALESCE(delivery_all, 0) - COALESCE(returning_all, 0) AS \"Gesamtleistung\"\nFROM LastValues\nORDER BY timestamp;\n",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "p1"
}
],
"title": "Aktueller Import oder Export",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "watth"
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 6,
"x": 12,
"y": 0
},
"id": 4,
"options": {
"legend": {
"calcs": [
"firstNotNull",
"lastNotNull",
"range"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "-- SELECT time_bucket('$__interval', timestamp) AS time, \n-- delivered_tariff1+delivered_tariff2 AS \"Import totaal\", \n-- returned_tariff1+returned_tariff2 AS \"Export totaal\", \n-- delivered_tariff1 AS \"Import telwerk 1\", \n-- delivered_tariff2 AS \"Import telwerk 2\", \n-- returned_tariff1 AS \"Export telwerk 1\", \n-- returned_tariff2 AS \"Export telwerk 2\"\n-- FROM p1\n-- WHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz\n\nWITH LastKnownValues AS (\n SELECT\n (SELECT delivered_tariff1 FROM p1 WHERE delivered_tariff1 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_delivered_tariff1,\n (SELECT delivered_tariff2 FROM p1 WHERE delivered_tariff2 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_delivered_tariff2,\n (SELECT returned_tariff1 FROM p1 WHERE returned_tariff1 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_returned_tariff1,\n (SELECT returned_tariff2 FROM p1 WHERE returned_tariff2 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_returned_tariff2\n),\nMainQuery AS (\n SELECT\n time_bucket('$__interval', timestamp) AS time,\n delivered_tariff1,\n delivered_tariff2,\n returned_tariff1,\n returned_tariff2\n FROM\n p1\n WHERE\n timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz\n)\nSELECT\n mq.time,\n (COALESCE(mq.delivered_tariff1, lv.last_delivered_tariff1) + COALESCE(mq.delivered_tariff2, lv.last_delivered_tariff2)) AS \"Import totaal\",\n (COALESCE(mq.returned_tariff1, lv.last_returned_tariff1) + COALESCE(mq.returned_tariff2, lv.last_returned_tariff2)) AS \"Export totaal\",\n COALESCE(mq.delivered_tariff1, lv.last_delivered_tariff1) AS \"Import Zählwerk 1\",\n COALESCE(mq.delivered_tariff2, lv.last_delivered_tariff2) AS \"Import Zählwerk 2\",\n COALESCE(mq.returned_tariff1, lv.last_returned_tariff1) AS \"Export Zählwerk 1\",\n COALESCE(mq.returned_tariff2, lv.last_returned_tariff2) AS \"Export Zählwerk 2\"\nFROM\n MainQuery mq,\n LastKnownValues lv\n",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "p1"
}
],
"title": "Zählerstände Strom",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"description": " Erster + Letzter = seit 0, Bereich = aktueller Zeitraum",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "watth"
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 6,
"x": 18,
"y": 0
},
"id": 10,
"options": {
"legend": {
"calcs": [
"firstNotNull",
"lastNotNull",
"range"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "-- SELECT time_bucket('$__interval', timestamp) AS time, \n-- delivered_tariff1-returned_tariff1 AS \"Telwerk 1\", \n-- delivered_tariff2-returned_tariff2 AS \"Telwerk 2\", \n-- delivered_tariff1+delivered_tariff2-returned_tariff1-returned_tariff2 AS \"Totaal\"\n-- FROM p1\n-- WHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz\n\nWITH LastKnownValues AS (\n SELECT\n (SELECT delivered_tariff1 FROM p1 WHERE delivered_tariff1 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_delivered_tariff1,\n (SELECT delivered_tariff2 FROM p1 WHERE delivered_tariff2 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_delivered_tariff2,\n (SELECT returned_tariff1 FROM p1 WHERE returned_tariff1 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_returned_tariff1,\n (SELECT returned_tariff2 FROM p1 WHERE returned_tariff2 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_returned_tariff2\n),\nMainQuery AS (\n SELECT\n time_bucket('$__interval', timestamp) AS time,\n COALESCE(delivered_tariff1, (SELECT last_delivered_tariff1 FROM LastKnownValues)) AS delivered_tariff1,\n COALESCE(delivered_tariff2, (SELECT last_delivered_tariff2 FROM LastKnownValues)) AS delivered_tariff2,\n COALESCE(returned_tariff1, (SELECT last_returned_tariff1 FROM LastKnownValues)) AS returned_tariff1,\n COALESCE(returned_tariff2, (SELECT last_returned_tariff2 FROM LastKnownValues)) AS returned_tariff2\n FROM\n p1\n WHERE\n timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz\n)\nSELECT\n time,\n (delivered_tariff1 - returned_tariff1) AS \"Zählwerk 1\",\n (delivered_tariff2 - returned_tariff2) AS \"Zählwerk 2\",\n (delivered_tariff1 + delivered_tariff2 - returned_tariff1 - returned_tariff2) AS \"Gesamt\"\nFROM\n MainQuery\n",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "p1"
}
],
"title": "Saldiert Zählerstände",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"description": "Min: 216,2V. Max: 253V. (-6% bis +10%)",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "volt"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 10
},
"id": 3,
"options": {
"legend": {
"calcs": [
"logmin",
"max",
"mean",
"lastNotNull"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT time_bucket('$__interval', timestamp) AS time, \nvoltage_l1 AS \"Spannung L1\", \nvoltage_l2 AS \"Spannung L2\", \nvoltage_l3 AS \"Spannung L3\"\nFROM p1\nWHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "p1"
}
],
"title": "Spannung",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "amp"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 12,
"y": 10
},
"id": 6,
"options": {
"legend": {
"calcs": [
"min",
"max",
"mean",
"lastNotNull"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT time_bucket('$__interval', timestamp) AS time, \ncurrent_l1 AS \"Leistung L1\", \ncurrent_l2 AS \"Leistung L2\", \ncurrent_l3 AS \"Leistung L3\"\nFROM p1\nWHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "p1"
}
],
"title": "Leistung",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 19
},
"id": 7,
"options": {
"legend": {
"calcs": [
"firstNotNull",
"lastNotNull",
"range"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT time_bucket('$__interval', timestamp) AS time, \nfailures AS \"Stromausfall\", \nlong_failures AS \"Längerer Stromausfall\" \nFROM p1\nWHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
}
}
],
"title": "Stromausfall",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"description": "1L = 1 dm³, also 1kL = 1m³\nBereich = Verbrauch im ausgewählten Zeitraum",
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "yellow",
"mode": "fixed"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 50,
"gradientMode": "opacity",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "stepAfter",
"lineStyle": {
"fill": "solid"
},
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"fieldMinMax": false,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "litre"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 19
},
"id": 5,
"options": {
"legend": {
"calcs": [
"firstNotNull",
"lastNotNull",
"range"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT time_bucket('$__interval', timestamp) AS time, \ngas AS \"Gasverbruik\"\nFROM p1\nWHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz AND gas IS NOT NULL",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
}
}
],
"title": "Gasverbrauch",
"type": "timeseries"
}
],
"refresh": "5s",
"schemaVersion": 39,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now/d",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "P1-Messdaten",
"uid": "cf296851-06f4-4479-9d5e-2bf85b56f69d",
"version": 3,
"weekStart": ""
}

View file

@ -1,902 +0,0 @@
{
"__inputs": [
{
"name": "DS_P1",
"label": "p1",
"description": "",
"type": "datasource",
"pluginId": "grafana-postgresql-datasource",
"pluginName": "PostgreSQL"
}
],
"__elements": {},
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "11.1.1"
},
{
"type": "datasource",
"id": "grafana-postgresql-datasource",
"name": "PostgreSQL",
"version": "1.0.0"
},
{
"type": "panel",
"id": "timeseries",
"name": "Time series",
"version": ""
}
],
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "watt"
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 12,
"x": 0,
"y": 0
},
"id": 8,
"options": {
"legend": {
"calcs": [
"min",
"max",
"mean",
"stdDev",
"last"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "-- SELECT time_bucket('$__interval', timestamp) AS time, \n-- delivery_l1 - returning_l1 AS \"Power L1\", \n-- delivery_l2 - returning_l2 AS \"Power L2\", \n-- delivery_l3 - returning_l3 AS \"Power L3\",\n-- delivery_all - returning_all AS \"Power total\"\n-- FROM p1\n-- WHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz\n\nWITH RECURSIVE DateRange AS (\n SELECT generate_series($__timeFrom()::timestamp, $__timeTo()::timestamp, '$__interval') AS timestamp\n),\nLastValues AS (\n SELECT\n dr.timestamp,\n (SELECT delivery_l1 FROM p1 WHERE timestamp <= dr.timestamp AND delivery_l1 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS delivery_l1,\n (SELECT delivery_l2 FROM p1 WHERE timestamp <= dr.timestamp AND delivery_l2 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS delivery_l2,\n (SELECT delivery_l3 FROM p1 WHERE timestamp <= dr.timestamp AND delivery_l3 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS delivery_l3,\n (SELECT delivery_all FROM p1 WHERE timestamp <= dr.timestamp AND delivery_all IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS delivery_all,\n (SELECT returning_l1 FROM p1 WHERE timestamp <= dr.timestamp AND returning_l1 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS returning_l1,\n (SELECT returning_l2 FROM p1 WHERE timestamp <= dr.timestamp AND returning_l2 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS returning_l2,\n (SELECT returning_l3 FROM p1 WHERE timestamp <= dr.timestamp AND returning_l3 IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS returning_l3,\n (SELECT returning_all FROM p1 WHERE timestamp <= dr.timestamp AND returning_all IS NOT NULL ORDER BY timestamp DESC LIMIT 1) AS returning_all\n FROM DateRange dr\n)\nSELECT\n timestamp,\n COALESCE(delivery_l1, 0) - COALESCE(returning_l1, 0) AS \"Power L1\",\n COALESCE(delivery_l2, 0) - COALESCE(returning_l2, 0) AS \"Power L2\",\n COALESCE(delivery_l3, 0) - COALESCE(returning_l3, 0) AS \"Power L3\",\n COALESCE(delivery_all, 0) - COALESCE(returning_all, 0) AS \"Power total\"\nFROM LastValues\nORDER BY timestamp;\n",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "p1"
}
],
"title": "Current import or export",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "watth"
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 6,
"x": 12,
"y": 0
},
"id": 4,
"options": {
"legend": {
"calcs": [
"firstNotNull",
"lastNotNull",
"range"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "-- SELECT time_bucket('$__interval', timestamp) AS time, \n-- delivered_tariff1+delivered_tariff2 AS \"Import total\", \n-- returned_tariff1+returned_tariff2 AS \"Export total\", \n-- delivered_tariff1 AS \"Import register 1\", \n-- delivered_tariff2 AS \"Import register 2\", \n-- returned_tariff1 AS \"Export register 1\", \n-- returned_tariff2 AS \"Export register 2\"\n-- FROM p1\n-- WHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz\n\nWITH LastKnownValues AS (\n SELECT\n (SELECT delivered_tariff1 FROM p1 WHERE delivered_tariff1 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_delivered_tariff1,\n (SELECT delivered_tariff2 FROM p1 WHERE delivered_tariff2 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_delivered_tariff2,\n (SELECT returned_tariff1 FROM p1 WHERE returned_tariff1 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_returned_tariff1,\n (SELECT returned_tariff2 FROM p1 WHERE returned_tariff2 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_returned_tariff2\n),\nMainQuery AS (\n SELECT\n time_bucket('$__interval', timestamp) AS time,\n delivered_tariff1,\n delivered_tariff2,\n returned_tariff1,\n returned_tariff2\n FROM\n p1\n WHERE\n timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz\n)\nSELECT\n mq.time,\n (COALESCE(mq.delivered_tariff1, lv.last_delivered_tariff1) + COALESCE(mq.delivered_tariff2, lv.last_delivered_tariff2)) AS \"Import total\",\n (COALESCE(mq.returned_tariff1, lv.last_returned_tariff1) + COALESCE(mq.returned_tariff2, lv.last_returned_tariff2)) AS \"Export total\",\n COALESCE(mq.delivered_tariff1, lv.last_delivered_tariff1) AS \"Import register 1\",\n COALESCE(mq.delivered_tariff2, lv.last_delivered_tariff2) AS \"Import register 2\",\n COALESCE(mq.returned_tariff1, lv.last_returned_tariff1) AS \"Export register 1\",\n COALESCE(mq.returned_tariff2, lv.last_returned_tariff2) AS \"Export register 2\"\nFROM\n MainQuery mq,\n LastKnownValues lv\n",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "p1"
}
],
"title": "Meter readings electricity",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"description": "First + Last = since 0, Range = current time window.",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "watth"
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 6,
"x": 18,
"y": 0
},
"id": 10,
"options": {
"legend": {
"calcs": [
"firstNotNull",
"lastNotNull",
"range"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "-- SELECT time_bucket('$__interval', timestamp) AS time, \n-- delivered_tariff1-returned_tariff1 AS \"Register 1\", \n-- delivered_tariff2-returned_tariff2 AS \"Register 2\", \n-- delivered_tariff1+delivered_tariff2-returned_tariff1-returned_tariff2 AS \"Total\"\n-- FROM p1\n-- WHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz\n\nWITH LastKnownValues AS (\n SELECT\n (SELECT delivered_tariff1 FROM p1 WHERE delivered_tariff1 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_delivered_tariff1,\n (SELECT delivered_tariff2 FROM p1 WHERE delivered_tariff2 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_delivered_tariff2,\n (SELECT returned_tariff1 FROM p1 WHERE returned_tariff1 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_returned_tariff1,\n (SELECT returned_tariff2 FROM p1 WHERE returned_tariff2 IS NOT NULL AND timestamp < $__timeFrom()::timestamptz ORDER BY timestamp DESC LIMIT 1) AS last_returned_tariff2\n),\nMainQuery AS (\n SELECT\n time_bucket('$__interval', timestamp) AS time,\n COALESCE(delivered_tariff1, (SELECT last_delivered_tariff1 FROM LastKnownValues)) AS delivered_tariff1,\n COALESCE(delivered_tariff2, (SELECT last_delivered_tariff2 FROM LastKnownValues)) AS delivered_tariff2,\n COALESCE(returned_tariff1, (SELECT last_returned_tariff1 FROM LastKnownValues)) AS returned_tariff1,\n COALESCE(returned_tariff2, (SELECT last_returned_tariff2 FROM LastKnownValues)) AS returned_tariff2\n FROM\n p1\n WHERE\n timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz\n)\nSELECT\n time,\n (delivered_tariff1 - returned_tariff1) AS \"Register 1\",\n (delivered_tariff2 - returned_tariff2) AS \"Register 2\",\n (delivered_tariff1 + delivered_tariff2 - returned_tariff1 - returned_tariff2) AS \"Total\"\nFROM\n MainQuery\n",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "p1"
}
],
"title": "Meter readings (net metered)",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"description": "Min: 216,2V. Max: 253V. (-6% to +10%)",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "volt"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 10
},
"id": 3,
"options": {
"legend": {
"calcs": [
"logmin",
"max",
"mean",
"lastNotNull"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT time_bucket('$__interval', timestamp) AS time, \nvoltage_l1 AS \"Voltage L1\", \nvoltage_l2 AS \"Voltage L2\", \nvoltage_l3 AS \"Voltage L3\"\nFROM p1\nWHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "p1"
}
],
"title": "Voltage",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "amp"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 12,
"y": 10
},
"id": 6,
"options": {
"legend": {
"calcs": [
"min",
"max",
"mean",
"lastNotNull"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT time_bucket('$__interval', timestamp) AS time, \ncurrent_l1 AS \"Current L1\", \ncurrent_l2 AS \"Current L2\", \ncurrent_l3 AS \"Current L3\"\nFROM p1\nWHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "p1"
}
],
"title": "Current",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 19
},
"id": 7,
"options": {
"legend": {
"calcs": [
"firstNotNull",
"lastNotNull",
"range"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT time_bucket('$__interval', timestamp) AS time, \nfailures AS \"Power outages\", \nlong_failures AS \"Long power outages\" \nFROM p1\nWHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
}
}
],
"title": "Power outages",
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"description": "1L = 1 dm^3, so 1kL = 1m^3. \nRange = consumption in selected time window.",
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "yellow",
"mode": "fixed"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 50,
"gradientMode": "opacity",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "stepAfter",
"lineStyle": {
"fill": "solid"
},
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"fieldMinMax": false,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "litre"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 19
},
"id": 5,
"options": {
"legend": {
"calcs": [
"firstNotNull",
"lastNotNull",
"range"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "${DS_P1}"
},
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT time_bucket('$__interval', timestamp) AS time, \ngas AS \"Gas consumption\"\nFROM p1\nWHERE timestamp >= $__timeFrom()::timestamptz AND timestamp < $__timeTo()::timestamptz AND gas IS NOT NULL",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
}
}
],
"title": "Gas consumption",
"type": "timeseries"
}
],
"refresh": "5s",
"schemaVersion": 39,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now/d",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "P1 meter readings",
"uid": "cf296851-06f4-4479-9d5e-2bf85b56f69c",
"version": 2,
"weekStart": ""
}

View file

@ -1,35 +1,4 @@
{ {
"__inputs": [
{
"name": "DS_P1",
"label": "p1",
"description": "",
"type": "datasource",
"pluginId": "grafana-postgresql-datasource",
"pluginName": "PostgreSQL"
}
],
"__elements": {},
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "11.1.1"
},
{
"type": "datasource",
"id": "grafana-postgresql-datasource",
"name": "PostgreSQL",
"version": "1.0.0"
},
{
"type": "panel",
"id": "timeseries",
"name": "Time series",
"version": ""
}
],
"annotations": { "annotations": {
"list": [ "list": [
{ {
@ -49,14 +18,14 @@
"editable": true, "editable": true,
"fiscalYearStartMonth": 0, "fiscalYearStartMonth": 0,
"graphTooltip": 0, "graphTooltip": 0,
"id": null, "id": 4,
"links": [], "links": [],
"liveNow": false, "liveNow": false,
"panels": [ "panels": [
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"fieldConfig": { "fieldConfig": {
"defaults": { "defaults": {
@ -141,8 +110,8 @@
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "postgres",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"editorMode": "code", "editorMode": "code",
"format": "table", "format": "table",
@ -175,7 +144,7 @@
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"fieldConfig": { "fieldConfig": {
"defaults": { "defaults": {
@ -258,8 +227,8 @@
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "postgres",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"editorMode": "code", "editorMode": "code",
"format": "table", "format": "table",
@ -292,7 +261,7 @@
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"description": "First + Last = sinds 0, Range = huidige tijdsperiode.", "description": "First + Last = sinds 0, Range = huidige tijdsperiode.",
"fieldConfig": { "fieldConfig": {
@ -376,8 +345,8 @@
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "postgres",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"editorMode": "code", "editorMode": "code",
"format": "table", "format": "table",
@ -409,8 +378,8 @@
}, },
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "postgres",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"description": "Min: 216,2V. Max: 253V. (-6% tot +10%)", "description": "Min: 216,2V. Max: 253V. (-6% tot +10%)",
"fieldConfig": { "fieldConfig": {
@ -495,8 +464,8 @@
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "postgres",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"editorMode": "code", "editorMode": "code",
"format": "table", "format": "table",
@ -528,8 +497,8 @@
}, },
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "postgres",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"description": "Hoofdaansluiting: 3*25A", "description": "Hoofdaansluiting: 3*25A",
"fieldConfig": { "fieldConfig": {
@ -614,8 +583,8 @@
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "postgres",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"editorMode": "code", "editorMode": "code",
"format": "table", "format": "table",
@ -648,7 +617,7 @@
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"fieldConfig": { "fieldConfig": {
"defaults": { "defaults": {
@ -730,8 +699,8 @@
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "postgres",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"editorMode": "code", "editorMode": "code",
"format": "table", "format": "table",
@ -763,7 +732,7 @@
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"description": "1L = 1 dm^3 dus 1kL = 1m^3. \nRange = verbruik in geselecteerde periode.", "description": "1L = 1 dm^3 dus 1kL = 1m^3. \nRange = verbruik in geselecteerde periode.",
"fieldConfig": { "fieldConfig": {
@ -852,8 +821,8 @@
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
"type": "grafana-postgresql-datasource", "type": "postgres",
"uid": "${DS_P1}" "uid": "e2a4c5ce-511e-4a7d-acb7-fcbb79b230bf"
}, },
"editorMode": "code", "editorMode": "code",
"format": "table", "format": "table",
@ -890,8 +859,8 @@
"list": [] "list": []
}, },
"time": { "time": {
"from": "now/d", "from": "now-2d/d",
"to": "now" "to": "now-2d/d"
}, },
"timepicker": {}, "timepicker": {},
"timezone": "", "timezone": "",

153
main.go
View file

@ -1,13 +1,9 @@
// TODO: Process p1/online status.
// TODO: Add health check endpoint.
// TODO: Add multiple P1 monitoring capabilities
package main package main
import ( import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/fs"
"log" "log"
"log/slog" "log/slog"
"os" "os"
@ -18,9 +14,6 @@ import (
mqtt "github.com/eclipse/paho.mqtt.golang" mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/joho/godotenv" "github.com/joho/godotenv"
_ "github.com/lib/pq" _ "github.com/lib/pq"
"github.com/pressly/goose/v3"
"git.hollander.online/energy/p1-logger/migrations"
) )
// Payload struct // Payload struct
@ -100,12 +93,6 @@ type Payload struct {
R *int `json:"r"` // Returning / exporting (W) R *int `json:"r"` // Returning / exporting (W)
F *int `json:"f"` // Failure (counter) F *int `json:"f"` // Failure (counter)
Fl *int `json:"fl"` // Failure long duration (counter) Fl *int `json:"fl"` // Failure long duration (counter)
Sa1 *int `json:"sa1"` // Number of voltage sags L1
Sa2 *int `json:"sa2"` // Number of voltage sags L2
Sa3 *int `json:"sa3"` // Number of voltage sags L3
Sw1 *int `json:"sw1"` // Number of voltage swells L1
Sw2 *int `json:"sw2"` // Number of voltage swells L1
Sw3 *int `json:"sw3"` // Number of voltage swells L1
G *int `json:"g"` // Gas meter reading (l) G *int `json:"g"` // Gas meter reading (l)
V1 int `json:"v1"` // Voltage L1 (V) V1 int `json:"v1"` // Voltage L1 (V)
V2 int `json:"v2"` // Voltage L2 (V) V2 int `json:"v2"` // Voltage L2 (V)
@ -119,14 +106,6 @@ type Payload struct {
R1 *int `json:"r1"` // Returning / exporting L1 (W) R1 *int `json:"r1"` // Returning / exporting L1 (W)
R2 *int `json:"r2"` // Returning / exporting L2 (W) R2 *int `json:"r2"` // Returning / exporting L2 (W)
R3 *int `json:"r3"` // Returning / exporting L3 (W) R3 *int `json:"r3"` // Returning / exporting L3 (W)
GBe *int `json:"gbe"` // Gas meter reading (l) (Belgium)
CAQDBe *int `json:"aeicad"` // Current Average Quarterly Demand for Peak Tariff (Belgium)
CMMQDBe *int `json:"cmmqd"` // Current Months Maximum Quarterly Demand for Peak Tarrif (Belgium)
TMMQDBe *int `json:"13mmqd"` // 13 Month Maximum Quarterly Demand for Peak Tarrif (Belgium).
DLu *int `json:"dlu"` // Energy Delivered (Luxembourg)
RLu *int `json:"rlu"` // Energy Returned (Luxembourg)
} }
type Config struct { type Config struct {
@ -302,11 +281,8 @@ func safeDerefInt(ptr *int) string {
} }
var prevDt1, prevDt2, prevRt1, prevRt2, prevG, prevF, prevFl int var prevDt1, prevDt2, prevRt1, prevRt2, prevG, prevF, prevFl int
var prevSa1, prevSa2, prevSa3, prevSw1, prevSw2, prevSw3 int
var prevD, prevR int var prevD, prevR int
var prevD1, prevD2, prevD3, prevR1, prevR2, prevR3 int var prevD1, prevD2, prevD3, prevR1, prevR2, prevR3 int
var prevGBe, prevCAQDBe, prevCMMQDBe, prevTMMQDBe int
var prevDLu, prevRLu int
func mqttMessageHandler(client mqtt.Client, msg mqtt.Message) { func mqttMessageHandler(client mqtt.Client, msg mqtt.Message) {
// Parse JSON payload // Parse JSON payload
@ -348,22 +324,6 @@ func mqttMessageHandler(client mqtt.Client, msg mqtt.Message) {
payload.Fl, tempChanged = updateFieldIfChanged(payload.Fl, &prevFl) payload.Fl, tempChanged = updateFieldIfChanged(payload.Fl, &prevFl)
changed = changed || tempChanged changed = changed || tempChanged
// Sags
payload.Sa1, tempChanged = updateFieldIfChanged(payload.Sa1, &prevSa1)
changed = changed || tempChanged
payload.Sa2, tempChanged = updateFieldIfChanged(payload.Sa2, &prevSa2)
changed = changed || tempChanged
payload.Sa3, tempChanged = updateFieldIfChanged(payload.Sa3, &prevSa3)
changed = changed || tempChanged
// Swells
payload.Sw1, tempChanged = updateFieldIfChanged(payload.Sw1, &prevSw1)
changed = changed || tempChanged
payload.Sw2, tempChanged = updateFieldIfChanged(payload.Sw2, &prevSw2)
changed = changed || tempChanged
payload.Sw3, tempChanged = updateFieldIfChanged(payload.Sw3, &prevSw3)
changed = changed || tempChanged
// Gas // Gas
payload.G, tempChanged = updateFieldIfChanged(payload.G, &prevG) payload.G, tempChanged = updateFieldIfChanged(payload.G, &prevG)
changed = changed || tempChanged changed = changed || tempChanged
@ -390,22 +350,6 @@ func mqttMessageHandler(client mqtt.Client, msg mqtt.Message) {
payload.R3, tempChanged = updateFieldIfChanged(payload.R3, &prevR3) payload.R3, tempChanged = updateFieldIfChanged(payload.R3, &prevR3)
changed = changed || tempChanged changed = changed || tempChanged
// Belgium
payload.GBe, tempChanged = updateFieldIfChanged(payload.GBe, &prevGBe)
changed = changed || tempChanged
payload.CAQDBe, tempChanged = updateFieldIfChanged(payload.CAQDBe, &prevCAQDBe)
changed = changed || tempChanged
payload.CMMQDBe, tempChanged = updateFieldIfChanged(payload.CMMQDBe, &prevCMMQDBe)
changed = changed || tempChanged
payload.TMMQDBe, tempChanged = updateFieldIfChanged(payload.TMMQDBe, &prevTMMQDBe)
changed = changed || tempChanged
// Luxembourg
payload.DLu, tempChanged = updateFieldIfChanged(payload.DLu, &prevDLu)
changed = changed || tempChanged
payload.RLu, tempChanged = updateFieldIfChanged(payload.RLu, &prevRLu)
changed = changed || tempChanged
// If any value has changed, log all the relevant values // If any value has changed, log all the relevant values
if changed { if changed {
logger.Debug("Values changed", logger.Debug("Values changed",
@ -419,15 +363,6 @@ func mqttMessageHandler(client mqtt.Client, msg mqtt.Message) {
"f", safeDerefInt(payload.F), "f", safeDerefInt(payload.F),
"fl", safeDerefInt(payload.Fl), "fl", safeDerefInt(payload.Fl),
"sa1", safeDerefInt(payload.Sa1),
"sa2", safeDerefInt(payload.Sa2),
"sa3", safeDerefInt(payload.Sa3),
"sw1", safeDerefInt(payload.Sw1),
"sw2", safeDerefInt(payload.Sw2),
"sw3", safeDerefInt(payload.Sw3),
"g", safeDerefInt(payload.G), "g", safeDerefInt(payload.G),
"d1", safeDerefInt(payload.D1), "d1", safeDerefInt(payload.D1),
@ -437,14 +372,6 @@ func mqttMessageHandler(client mqtt.Client, msg mqtt.Message) {
"r1", safeDerefInt(payload.R1), "r1", safeDerefInt(payload.R1),
"r2", safeDerefInt(payload.R2), "r2", safeDerefInt(payload.R2),
"r3", safeDerefInt(payload.R3), "r3", safeDerefInt(payload.R3),
"gbe", safeDerefInt(payload.GBe),
"caqdbe", safeDerefInt(payload.CAQDBe),
"cmmqdbe", safeDerefInt(payload.CMMQDBe),
"tmmqdbe", safeDerefInt(payload.TMMQDBe),
"dlu", safeDerefInt(payload.DLu),
"rlu", safeDerefInt(payload.RLu),
) )
} }
// Insert data into PostgreSQL // Insert data into PostgreSQL
@ -471,17 +398,7 @@ func mqttMessageHandler(client mqtt.Client, msg mqtt.Message) {
"d3", payload.D3, "d3", payload.D3,
"r1", payload.R1, "r1", payload.R1,
"r2", payload.R2, "r2", payload.R2,
"r3", payload.R3, "r3", payload.R3)
"gbe", payload.GBe,
"caqdbe", payload.CAQDBe,
"cmmqdbe", payload.CMMQDBe,
"tmmqdbe", payload.TMMQDBe,
"dlu", payload.DLu,
"rlu", payload.RLu,
)
if err != nil { if err != nil {
logger.Error("Error inserting data into PostgreSQL", "error", err) logger.Error("Error inserting data into PostgreSQL", "error", err)
} }
@ -518,16 +435,12 @@ func insertData(timestamp time.Time, payload Payload) error {
stmt := ` stmt := `
INSERT INTO p1 ( INSERT INTO p1 (
timestamp, delivered_tariff1, delivered_tariff2, returned_tariff1, returned_tariff2, timestamp, delivered_tariff1, delivered_tariff2, returned_tariff1, returned_tariff2,
delivery_all, returning_all, delivery_all, returning_all, failures, long_failures, gas,
failures, long_failures,
sags_l1, sags_l2, sags_l3,
swells_l1, swells_l2, swells_l3,
gas,
voltage_l1, voltage_l2, voltage_l3, voltage_l1, voltage_l2, voltage_l3,
current_l1, current_l2, current_l3, current_l1, current_l2, current_l3,
delivery_l1, delivery_l2, delivery_l3, delivery_l1, delivery_l2, delivery_l3,
returning_l1, returning_l2, returning_l3 returning_l1, returning_l2, returning_l3
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28) ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)
` `
_, err := db.Exec( _, err := db.Exec(
stmt, stmt,
@ -536,8 +449,6 @@ func insertData(timestamp time.Time, payload Payload) error {
payload.Rt1, payload.Rt2, payload.Rt1, payload.Rt2,
payload.D, payload.R, payload.D, payload.R,
payload.F, payload.Fl, payload.F, payload.Fl,
payload.Sa1, payload.Sa2, payload.Sa3,
payload.Sw1, payload.Sw2, payload.Sw3,
payload.G, payload.G,
payload.V1, payload.V2, payload.V3, payload.V1, payload.V2, payload.V3,
payload.C1, payload.C2, payload.C3, payload.C1, payload.C2, payload.C3,
@ -560,10 +471,36 @@ func connectToPostgreSQL(pgConnStr string) error {
time.Sleep(5 * time.Second) // Retry after 5 seconds time.Sleep(5 * time.Second) // Retry after 5 seconds
} }
// Perform DB migrations // Create table if not exists
err = migrateFS(db, migrations.FS, ".") _, err = db.Exec(`
CREATE TABLE IF NOT EXISTS p1 (
timestamp TIMESTAMPTZ,
delivered_tariff1 INT,
delivered_tariff2 INT,
returned_tariff1 INT,
returned_tariff2 INT,
delivery_all INT,
returning_all INT,
failures INT,
long_failures INT,
gas INT,
voltage_l1 INT,
voltage_l2 INT,
voltage_l3 INT,
current_l1 INT,
current_l2 INT,
current_l3 INT,
delivery_l1 INT,
delivery_l2 INT,
delivery_l3 INT,
returning_l1 INT,
returning_l2 INT,
returning_l3 INT
);
-- CREATE UNIQUE INDEX IF NOT EXISTS timestamp_idx ON p1 (timestamp);
`)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal("Error creating table:", err)
} }
if config.TimescaleDB { if config.TimescaleDB {
@ -579,29 +516,3 @@ func connectToPostgreSQL(pgConnStr string) error {
return nil return nil
} }
func migrate(db *sql.DB, dir string) error {
err := goose.SetDialect("postgres")
if err != nil {
return fmt.Errorf("migrate: %w", err)
}
err = goose.Up(db, dir)
if err != nil {
return fmt.Errorf("migrate: %w", err)
}
return nil
}
func migrateFS(db *sql.DB, migrationFS fs.FS, dir string) error {
// In case the dir is an empty string, they probably meant the current directory and goose wants a period for that.
if dir == "" {
dir = "."
}
goose.SetBaseFS(migrationFS)
defer func() {
// Ensure that we remove the FS on the off chance some other part of our app uses goose for migrations and doesn't want to use our FS.
goose.SetBaseFS(nil)
}()
return migrate(db, dir)
}

View file

@ -1,33 +0,0 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS p1 (
timestamp TIMESTAMPTZ,
delivered_tariff1 INT,
delivered_tariff2 INT,
returned_tariff1 INT,
returned_tariff2 INT,
delivery_all INT,
returning_all INT,
failures INT,
long_failures INT,
sags_l1 INT,
sags_l2 INT,
sags_l3 INT,
swells_l1 INT,
swells_l2 INT,
swells_l3 INT,
gas INT,
voltage_l1 INT,
voltage_l2 INT,
voltage_l3 INT,
current_l1 INT,
current_l2 INT,
current_l3 INT,
delivery_l1 INT,
delivery_l2 INT,
delivery_l3 INT,
returning_l1 INT,
returning_l2 INT,
returning_l3 INT
);
-- +goose StatementEnd

View file

@ -1,6 +0,0 @@
package migrations
import "embed"
//go:embed *.sql
var FS embed.FS