Inital commit
This commit is contained in:
187
app/octopus_to_influxdb.py
Normal file
187
app/octopus_to_influxdb.py
Normal file
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from configparser import ConfigParser
|
||||
from urllib import parse
|
||||
|
||||
import click
|
||||
import maya
|
||||
import requests
|
||||
from influxdb import InfluxDBClient
|
||||
|
||||
|
||||
def retrieve_paginated_consumption(
|
||||
api_key, url, from_date, to_date, page=None
|
||||
):
|
||||
args = {
|
||||
'period_from': from_date,
|
||||
'period_to': to_date,
|
||||
}
|
||||
if page:
|
||||
args['page'] = page
|
||||
response = requests.get(url, params=args, auth=(api_key, ''))
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
results = data.get('results', [])
|
||||
if data['next']:
|
||||
url_query = parse.urlparse(data['next']).query
|
||||
next_page = parse.parse_qs(url_query)['page'][0]
|
||||
results += retrieve_paginated_consumption(
|
||||
api_key, url, from_date, to_date, next_page
|
||||
)
|
||||
return results
|
||||
|
||||
|
||||
def store_series(connection, series, metrics, rate_data):
|
||||
|
||||
def active_rate_field(measurement):
|
||||
if series == 'gas':
|
||||
return 'unit_rate'
|
||||
elif not rate_data['unit_rate_low_zone']: # no low rate
|
||||
return 'unit_rate_high'
|
||||
|
||||
low_start_str = rate_data['unit_rate_low_start']
|
||||
low_end_str = rate_data['unit_rate_low_end']
|
||||
low_zone = rate_data['unit_rate_low_zone']
|
||||
|
||||
measurement_at = maya.parse(measurement['interval_start'])
|
||||
|
||||
low_start = maya.when(
|
||||
measurement_at.datetime().strftime(f'%Y-%m-%dT{low_start_str}'),
|
||||
timezone=low_zone
|
||||
)
|
||||
low_end = maya.when(
|
||||
measurement_at.datetime().strftime(f'%Y-%m-%dT{low_end_str}'),
|
||||
timezone=low_zone
|
||||
)
|
||||
low_period = maya.MayaInterval(low_start, low_end)
|
||||
|
||||
return \
|
||||
'unit_rate_low' if measurement_at in low_period \
|
||||
else 'unit_rate_high'
|
||||
|
||||
def fields_for_measurement(measurement):
|
||||
consumption = measurement['consumption']
|
||||
rate = active_rate_field(measurement)
|
||||
rate_cost = rate_data[rate]
|
||||
cost = consumption * rate_cost
|
||||
standing_charge = rate_data['standing_charge'] / 48 # 30 minute reads
|
||||
return {
|
||||
'consumption': consumption,
|
||||
'cost': cost,
|
||||
'total_cost': cost + standing_charge,
|
||||
}
|
||||
|
||||
def tags_for_measurement(measurement):
|
||||
period = maya.parse(measurement['interval_end'])
|
||||
time = period.datetime().strftime('%H:%M')
|
||||
return {
|
||||
'active_rate': active_rate_field(measurement),
|
||||
'time_of_day': time,
|
||||
}
|
||||
|
||||
measurements = [
|
||||
{
|
||||
'measurement': series,
|
||||
'tags': tags_for_measurement(measurement),
|
||||
'time': measurement['interval_end'],
|
||||
'fields': fields_for_measurement(measurement),
|
||||
}
|
||||
for measurement in metrics
|
||||
]
|
||||
connection.write_points(measurements)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
'--config-file',
|
||||
default="octograph.ini",
|
||||
type=click.Path(exists=True, dir_okay=True, readable=True),
|
||||
)
|
||||
@click.option('--from-date', default='yesterday midnight', type=click.STRING)
|
||||
@click.option('--to-date', default='today midnight', type=click.STRING)
|
||||
def cmd(config_file, from_date, to_date):
|
||||
|
||||
config = ConfigParser()
|
||||
config.read(config_file)
|
||||
|
||||
influx = InfluxDBClient(
|
||||
host=config.get('influxdb', 'host', fallback="localhost"),
|
||||
port=config.getint('influxdb', 'port', fallback=8086),
|
||||
username=config.get('influxdb', 'user', fallback=""),
|
||||
password=config.get('influxdb', 'password', fallback=""),
|
||||
database=config.get('influxdb', 'database', fallback="energy"),
|
||||
)
|
||||
|
||||
api_key = config.get('octopus', 'api_key')
|
||||
if not api_key:
|
||||
raise click.ClickException('No Octopus API key set')
|
||||
|
||||
e_mpan = config.get('electricity', 'mpan', fallback=None)
|
||||
e_serial = config.get('electricity', 'serial_number', fallback=None)
|
||||
if not e_mpan or not e_serial:
|
||||
raise click.ClickException('No electricity meter identifiers')
|
||||
e_url = 'https://api.octopus.energy/v1/electricity-meter-points/' \
|
||||
f'{e_mpan}/meters/{e_serial}/consumption/'
|
||||
|
||||
g_mpan = config.get('gas', 'mpan', fallback=None)
|
||||
g_serial = config.get('gas', 'serial_number', fallback=None)
|
||||
if not g_mpan or not g_serial:
|
||||
raise click.ClickException('No gas meter identifiers')
|
||||
g_url = 'https://api.octopus.energy/v1/gas-meter-points/' \
|
||||
f'{g_mpan}/meters/{g_serial}/consumption/'
|
||||
|
||||
rate_data = {
|
||||
'electricity': {
|
||||
'standing_charge': config.getfloat(
|
||||
'electricity', 'standing_charge', fallback=0.0
|
||||
),
|
||||
'unit_rate_high': config.getfloat(
|
||||
'electricity', 'unit_rate_high', fallback=0.0
|
||||
),
|
||||
'unit_rate_low': config.getfloat(
|
||||
'electricity', 'unit_rate_low', fallback=0.0
|
||||
),
|
||||
'unit_rate_low_start': config.get(
|
||||
'electricity', 'unit_rate_low_start', fallback="00:00"
|
||||
),
|
||||
'unit_rate_low_end': config.get(
|
||||
'electricity', 'unit_rate_low_end', fallback="00:00"
|
||||
),
|
||||
'unit_rate_low_zone': config.get(
|
||||
'electricity', 'unit_rate_low_zone', fallback=None
|
||||
),
|
||||
},
|
||||
'gas': {
|
||||
'standing_charge': config.getfloat(
|
||||
'gas', 'standing_charge', fallback=0.0
|
||||
),
|
||||
'unit_rate': config.getfloat('gas', 'unit_rate', fallback=0.0),
|
||||
}
|
||||
}
|
||||
|
||||
from_iso = maya.when(from_date).iso8601()
|
||||
to_iso = maya.when(to_date).iso8601()
|
||||
|
||||
click.echo(
|
||||
f'Retrieving electricity data for {from_iso} until {to_iso}...',
|
||||
nl=False
|
||||
)
|
||||
e_consumption = retrieve_paginated_consumption(
|
||||
api_key, e_url, from_iso, to_iso
|
||||
)
|
||||
click.echo(f' {len(e_consumption)} readings.')
|
||||
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(
|
||||
api_key, g_url, from_iso, to_iso
|
||||
)
|
||||
click.echo(f' {len(g_consumption)} readings.')
|
||||
store_series(influx, 'gas', g_consumption, rate_data['gas'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cmd()
|
||||
Reference in New Issue
Block a user