diff --git a/app/octopus_to_influxdb.py b/app/octopus_to_influxdb.py index 5457963..cb70026 100644 --- a/app/octopus_to_influxdb.py +++ b/app/octopus_to_influxdb.py @@ -9,7 +9,7 @@ import requests from influxdb import InfluxDBClient -def retrieve_paginated_consumption( +def retrieve_paginated_data( api_key, url, from_date, to_date, page=None ): args = { @@ -25,7 +25,7 @@ def retrieve_paginated_consumption( if data['next']: url_query = parse.urlparse(data['next']).query next_page = parse.parse_qs(url_query)['page'][0] - results += retrieve_paginated_consumption( + results += retrieve_paginated_data( api_key, url, from_date, to_date, next_page ) return results @@ -33,6 +33,12 @@ def retrieve_paginated_consumption( def store_series(connection, series, metrics, rate_data): + agile_data = rate_data.get('agile_unit_rates', []) + agile_rates = { + point['valid_to']: point['value_inc_vat'] + for point in agile_data + } + def active_rate_field(measurement): if series == 'gas': return 'unit_rate' @@ -65,11 +71,24 @@ def store_series(connection, series, metrics, rate_data): rate_cost = rate_data[rate] cost = consumption * rate_cost standing_charge = rate_data['standing_charge'] / 48 # 30 minute reads - return { + fields = { 'consumption': consumption, 'cost': cost, 'total_cost': cost + standing_charge, } + if agile_data: + agile_standing_charge = rate_data['agile_standing_charge'] / 48 + agile_unit_rate = agile_rates.get( + measurement['interval_end'], + rate_data[rate] # cludge, use Go rate during DST changeover + ) + agile_cost = agile_unit_rate * consumption + fields.update({ + 'agile_rate': agile_unit_rate, + 'agile_cost': agile_cost, + 'agile_total_cost': agile_cost + agile_standing_charge, + }) + return fields def tags_for_measurement(measurement): period = maya.parse(measurement['interval_end']) @@ -122,6 +141,7 @@ def cmd(config_file, from_date, to_date): raise click.ClickException('No electricity meter identifiers') e_url = 'https://api.octopus.energy/v1/electricity-meter-points/' \ f'{e_mpan}/meters/{e_serial}/consumption/' + agile_url = config.get('electricity', 'agile_rate_url', fallback=None) g_mpan = config.get('gas', 'mpan', fallback=None) g_serial = config.get('gas', 'serial_number', fallback=None) @@ -150,6 +170,10 @@ def cmd(config_file, from_date, to_date): 'unit_rate_low_zone': config.get( 'electricity', 'unit_rate_low_zone', fallback=None ), + 'agile_standing_charge': config.getfloat( + 'electricity', 'agile_standing_charge', fallback=0.0 + ), + 'agile_unit_rates': [], }, 'gas': { 'standing_charge': config.getfloat( @@ -166,17 +190,25 @@ def cmd(config_file, from_date, to_date): f'Retrieving electricity data for {from_iso} until {to_iso}...', nl=False ) - e_consumption = retrieve_paginated_consumption( + e_consumption = retrieve_paginated_data( api_key, e_url, from_iso, to_iso ) click.echo(f' {len(e_consumption)} readings.') + click.echo( + f'Retrieving Agile rates for {from_iso} until {to_iso}...', + nl=False + ) + rate_data['electricity']['agile_unit_rates'] = retrieve_paginated_data( + api_key, agile_url, from_iso, to_iso + ) + click.echo(f' {len(rate_data["electricity"]["agile_unit_rates"])} rates.') store_series(influx, 'electricity', e_consumption, rate_data['electricity']) click.echo( f'Retrieving gas data for {from_iso} until {to_iso}...', nl=False ) - g_consumption = retrieve_paginated_consumption( + g_consumption = retrieve_paginated_data( api_key, g_url, from_iso, to_iso ) click.echo(f' {len(g_consumption)} readings.') diff --git a/energy_dashboard.json b/energy_dashboard.json index d3a54db..912b7c4 100644 --- a/energy_dashboard.json +++ b/energy_dashboard.json @@ -267,7 +267,7 @@ } ], "thresholds": "", - "title": "Electricity cost (units only)", + "title": "Go cost (units only)", "type": "singlestat", "valueFontSize": "80%", "valueMaps": [ @@ -380,7 +380,7 @@ } ], "thresholds": "", - "title": "Electricity cost (total)", + "title": "Go cost (total)", "type": "singlestat", "valueFontSize": "80%", "valueMaps": [ @@ -493,7 +493,346 @@ } ], "thresholds": "", - "title": "Electricity effective unit cost", + "title": "Go effective unit cost", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "total" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_INFLUX}", + "decimals": 2, + "format": "currencyGBP", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 2, + "w": 6, + "x": 6, + "y": 2 + }, + "id": 17, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "none" + ], + "type": "fill" + } + ], + "measurement": "electricity", + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT sum(\"agile_cost\")/100 FROM \"electricity\" WHERE $timeFilter", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "cost" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [] + } + ], + "thresholds": "", + "title": "Agile cost (units only)", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "total" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_INFLUX}", + "decimals": 2, + "format": "currencyGBP", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 2, + "w": 6, + "x": 12, + "y": 2 + }, + "id": 18, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "none" + ], + "type": "fill" + } + ], + "measurement": "electricity", + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT sum(\"agile_total_cost\")/100 FROM \"electricity\" WHERE $timeFilter", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "cost" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [] + } + ], + "thresholds": "", + "title": "Agile cost (total)", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "total" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_INFLUX}", + "decimals": 4, + "format": "currencyGBP", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 2, + "w": 6, + "x": 18, + "y": 2 + }, + "id": 19, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "none" + ], + "type": "fill" + } + ], + "measurement": "electricity", + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT (sum(\"agile_total_cost\") / sum(\"consumption\"))/100 FROM \"electricity\" WHERE $timeFilter", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "cost" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [] + } + ], + "thresholds": "", + "title": "Agile effective unit cost", "type": "singlestat", "valueFontSize": "80%", "valueMaps": [ @@ -516,7 +855,7 @@ "h": 9, "w": 24, "x": 0, - "y": 2 + "y": 4 }, "id": 2, "legend": { @@ -540,7 +879,12 @@ "renderer": "flot", "seriesOverrides": [ { - "alias": "Cost", + "alias": "Go cost", + "yaxis": 2, + "zindex": -1 + }, + { + "alias": "Agile cost", "yaxis": 2, "zindex": -1 } @@ -589,7 +933,7 @@ "tags": [] }, { - "alias": "Cost", + "alias": "Go cost", "groupBy": [ { "params": [ @@ -627,6 +971,46 @@ ] ], "tags": [] + }, + { + "alias": "Agile cost", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "linear" + ], + "type": "fill" + } + ], + "hide": false, + "measurement": "electricity", + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT mean(\"agile_cost\")/100 FROM \"electricity\" WHERE $timeFilter GROUP BY time(30m) fill(linear)", + "rawQuery": true, + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "cost" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] } ], "thresholds": [], @@ -657,7 +1041,7 @@ "show": true }, { - "decimals": null, + "decimals": 2, "format": "currencyGBP", "label": "Cost", "logBase": 1, @@ -693,7 +1077,7 @@ "h": 2, "w": 6, "x": 0, - "y": 11 + "y": 13 }, "id": 7, "interval": null, @@ -806,7 +1190,7 @@ "h": 2, "w": 6, "x": 6, - "y": 11 + "y": 13 }, "id": 11, "interval": null, @@ -919,7 +1303,7 @@ "h": 2, "w": 6, "x": 12, - "y": 11 + "y": 13 }, "id": 13, "interval": null, @@ -1032,7 +1416,7 @@ "h": 2, "w": 6, "x": 18, - "y": 11 + "y": 13 }, "id": 15, "interval": null, @@ -1133,7 +1517,7 @@ "h": 9, "w": 24, "x": 0, - "y": 13 + "y": 15 }, "id": 8, "legend": { @@ -1274,7 +1658,7 @@ "show": true }, { - "decimals": null, + "decimals": 2, "format": "currencyGBP", "label": "Cost", "logBase": 1, @@ -1297,8 +1681,8 @@ "list": [] }, "time": { - "from": "now-7d", - "to": "now" + "from": "now-8d", + "to": "now-1d" }, "timepicker": { "refresh_intervals": [ @@ -1328,5 +1712,5 @@ "timezone": "", "title": "Energy usage", "uid": "7Nqpoj-ik", - "version": 11 + "version": 14 } \ No newline at end of file diff --git a/example-octograph.ini b/example-octograph.ini index 1ed6079..811bf13 100644 --- a/example-octograph.ini +++ b/example-octograph.ini @@ -17,6 +17,8 @@ unit_rate_low = 5.00 unit_rate_low_start = 00:30 unit_rate_low_end = 04:30 unit_rate_low_zone = Europe/London +agile_standing_charge = 21.00 +agile_rate_url = https://api.octopus.energy/v1/products/AGILE-18-02-21/electricity-tariffs/E-1R-AGILE-18-02-21-A/standard-unit-rates/ [gas] mpan = 12345 diff --git a/grafana-dashboard.png b/grafana-dashboard.png index 6f3f477..4f566b4 100644 Binary files a/grafana-dashboard.png and b/grafana-dashboard.png differ