Skip to content
This repository has been archived by the owner on Jan 20, 2021. It is now read-only.

Commit

Permalink
Adding support for getting monthly and yearly gas usage and cost
Browse files Browse the repository at this point in the history
Note: This currently only supports gas tariffs which only return one
charge, rather than varying charges per time block:
```
{u'count': 1,
 u'next': None,
 u'previous': None,
 u'results': [{u'valid_from': None,
   u'valid_to': None,
   u'value_exc_vat': 2.72,
   u'value_inc_vat': 2.856}]}
```
  • Loading branch information
badguy99 committed Apr 25, 2020
1 parent 67daed2 commit 3c79fda
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 51 deletions.
42 changes: 29 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ sensor.octopus_yearly_usage
sensor.octopus_monthly_cost
sensor.octopus_monthly_usage
```
If can also pull monthly and yearly gas cost and usage, and have sensors for them set up, if the gas section is included in the yaml configuration:
```
sensor.octopus_yearly_gas_cost
sensor.octopus_yearly_gas_usage
sensor.octopus_monthly_gas_cost
sensor.octopus_monthly_gas_usage
```

The data is updated once every two hours, although in reality the data Octopus Energy gets only seems to be updated once a day, so this is a compromise between trying to be up-to-date, and not hammering their servers, when the data doesn't update very frequently anyway.

[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://github.com/custom-components/hacs)
Expand All @@ -25,21 +33,29 @@ octocost:
serial: <Serial number>
auth: <Octopus Energy API Key>
startdate: 2020-02-23

gas:
mprn: <Gas MPRN number>
gasserial: <Gas meter serial number>
gas_tariff: FIX-12M-20-02-12
gas_startdate: 2020-02-23
```
The module and class sections need to remain as above, other sections should be changed as required.

| Field | Changeable | Example |
| ----- | ---------- | ------- |
| Title | Yes | octocost |
| module | No | octocost |
| class | No | OctoCost |
| region | Yes | H |
| mpan | Yes | 2000012345678 |
| serial | Yes | 20L123456 |
| auth | Yes | sk_live_abcdefg |
| startdate | Yes | 2020-02-23 |
The module and class sections need to remain as above, other sections should be changed as required. The whole gas section is optional and can be excluded if not required.
| Field | Changeable | Example |
| ----- | ---------- | ------- |
| Title | Yes | octocost |
| module | No | octocost |
| class | No | OctoCost |
| region | Yes | H |
| mpan | Yes | 2000012345678 |
| serial | Yes | 20L123456 |
| auth | Yes | sk_live_abcdefg |
| startdate | Yes | 2020-02-23 |
| gas: | Yes | |
| mprn | Yes | 1234567890 |
| gasserial | Yes | E1S12345678901 |
| gas_tariff | Yes | FIX-12M-20-02-12 |
| gas_startdate | Yes | 2020-02-23 |
The `startdate` setting should be set to the date you started on the Agile Octopus tariff, not the date you joined Octopus Energy. It is used to adjust the start point if you joined within the current year or month, it should not be left blank if you joined earlier.
`region` is the region letter from the end of `E-1R-AGILE-18-02-21-H` which can be found on the [Octopus Energy developer dashboard](https://octopus.energy/dashboard/developer/) webpage in the Unit Rates section for your account.

Expand Down
134 changes: 96 additions & 38 deletions apps/octocost/octocost.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@ def initialize(self):
MPAN = self.args['mpan']
SERIAL = self.args['serial']
region = self.args.get('region', self.find_region(MPAN))

self.startdate = datetime.date.fromisoformat(
gas = self.args.get('gas', None)
if gas:
gas_tariff = gas.get('gas_tariff', None)
MPRN = gas.get('mprn', None)
GASSERIAL = gas.get('gasserial', None)
gasstartdate = datetime.date.fromisoformat(
str(gas.get('gas_startdate')))

elecstartdate = datetime.date.fromisoformat(
str(self.args['startdate']))

consumptionurl = 'https://api.octopus.energy/' + \
Expand All @@ -23,14 +30,35 @@ def initialize(self):
'AGILE-18-02-21/electricity-tariffs/E-1R-AGILE-18-02-21-' + \
str(region).upper() + '/standard-unit-rates/'

if gas:
gasconsumptionurl = 'https://api.octopus.energy/' + \
'v1/gas-meter-points/' + str(MPRN) + '/meters/' + \
str(GASSERIAL) + '/consumption/'
gascosturl = 'https://api.octopus.energy/v1/products/' + \
gas_tariff + '/gas-tariffs/G-1R-' + gas_tariff + '-' + \
str(region).upper() + '/standard-unit-rates/'

self.run_in(self.cost_and_usage_callback, 5,
use=consumptionurl, cost=costurl)
use=consumptionurl, cost=costurl, date=elecstartdate)
if gas:
self.run_in(self.cost_and_usage_callback, 5,
use=gasconsumptionurl, cost=gascosturl,
date=gasstartdate, gas=True)

for hour in [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]:
self.run_hourly(self.cost_and_usage_callback,
datetime.time(hour, 0, 0),
use=consumptionurl,
cost=costurl)
cost=costurl,
date=elecstartdate)

if gas:
self.run_hourly(self.cost_and_usage_callback,
datetime.time(hour, 0, 0),
use=gasconsumptionurl,
cost=gascosturl,
date=gasstartdate,
gas=True)

def find_region(mpan):
url = 'https://api.octopus.energy/v1/electricity-meter-points/' + \
Expand All @@ -43,6 +71,8 @@ def find_region(mpan):
def cost_and_usage_callback(self, kwargs):
self.useurl = kwargs.get('use')
self.costurl = kwargs.get('cost')
self.startdate = kwargs.get('date')
self.gas = kwargs.get('gas', False)
today = datetime.date.today()
self.yesterday = today - datetime.timedelta(days=1)
startyear = datetime.date(today.year, 1, 1)
Expand All @@ -56,30 +86,48 @@ def cost_and_usage_callback(self, kwargs):

monthlyusage, monthlycost = self.calculate_cost_and_usage(
start=startmonth)
print('Total monthly usage: {} kWh'.format(monthlyusage))
print('Total monthly usage: {}'.format(monthlyusage))
print('Total monthly cost: {} p'.format(monthlycost))

yearlyusage, yearlycost = self.calculate_cost_and_usage(
start=startyear)
print('Total yearly usage: {} kWh'.format(yearlyusage))
print('Total yearly usage: {}'.format(yearlyusage))
print('Total yearly cost: {} p'.format(yearlycost))

self.set_state('sensor.octopus_yearly_usage',
state=round(yearlyusage, 2),
attributes={'unit_of_measurement': 'kWh',
'icon': 'mdi:flash'})
self.set_state('sensor.octopus_yearly_cost',
state=round(yearlycost/100, 2),
attributes={'unit_of_measurement': '£',
'icon': 'mdi:cash'})
self.set_state('sensor.octopus_monthly_usage',
state=round(monthlyusage, 2),
attributes={'unit_of_measurement': 'kWh',
'icon': 'mdi:flash'})
self.set_state('sensor.octopus_monthly_cost',
state=round(monthlycost/100, 2),
attributes={'unit_of_measurement': '£',
'icon': 'mdi:cash'})
if not self.gas:
self.set_state('sensor.octopus_yearly_usage',
state=round(yearlyusage, 2),
attributes={'unit_of_measurement': 'kWh',
'icon': 'mdi:flash'})
self.set_state('sensor.octopus_yearly_cost',
state=round(yearlycost/100, 2),
attributes={'unit_of_measurement': '£',
'icon': 'mdi:cash'})
self.set_state('sensor.octopus_monthly_usage',
state=round(monthlyusage, 2),
attributes={'unit_of_measurement': 'kWh',
'icon': 'mdi:flash'})
self.set_state('sensor.octopus_monthly_cost',
state=round(monthlycost/100, 2),
attributes={'unit_of_measurement': '£',
'icon': 'mdi:cash'})
else:
self.set_state('sensor.octopus_yearly_gas_usage',
state=round(yearlyusage, 2),
attributes={'unit_of_measurement': 'm3',
'icon': 'mdi:fire'})
self.set_state('sensor.octopus_yearly_gas_cost',
state=round(yearlycost/100, 2),
attributes={'unit_of_measurement': '£',
'icon': 'mdi:cash'})
self.set_state('sensor.octopus_monthly_gas_usage',
state=round(monthlyusage, 2),
attributes={'unit_of_measurement': 'm3',
'icon': 'mdi:fire'})
self.set_state('sensor.octopus_monthly_gas_cost',
state=round(monthlycost/100, 2),
attributes={'unit_of_measurement': '£',
'icon': 'mdi:cash'})

def calculate_count(self, start):
numberdays = self.yesterday-start
Expand Down Expand Up @@ -122,24 +170,34 @@ def calculate_cost_and_usage(self, start):
for period in results:
curridx = results.index(period)
usage = usage + (results[curridx][u'consumption'])
if ((results[curridx][u'interval_start']) !=
(cost[curridx][u'valid_from'])):
# Daylight Savings?
consumption_date = (results[curridx][u'interval_start'])
if consumption_date.endswith('+01:00'):
date_time = dateutil.parser.parse(consumption_date)
utc_datetime = date_time.astimezone(utc)
utc_iso = utc_datetime.isoformat().replace("+00:00", "Z")
if utc_iso == (cost[curridx][u'valid_from']):
(results[curridx][u'interval_start']) = utc_iso
if not self.gas:
if ((results[curridx][u'interval_start']) !=
(cost[curridx][u'valid_from'])):
# Daylight Savings?
consumption_date = (results[curridx][u'interval_start'])
if consumption_date.endswith('+01:00'):
date_time = dateutil.parser.parse(consumption_date)
utc_datetime = date_time.astimezone(utc)
utc_iso = utc_datetime.isoformat().replace("+00:00", "Z")
if utc_iso == (cost[curridx][u'valid_from']):
(results[curridx][u'interval_start']) = utc_iso
else:
print('UTC Unmatched consumption {}'.format(
results[curridx][u'interval_start']) +
' / cost {}'.format(cost[curridx][u'valid_from']))
else:
print('UTC Unmatched consumption {}'.format(
print('Unmatched consumption {}'.format(
results[curridx][u'interval_start']) +
' / cost {}'.format(cost[curridx][u'valid_from']))
price = price + ((cost[curridx][u'value_inc_vat']) *
(results[curridx][u'consumption']))
else:
# Only dealing with gas price which doesn't vary at the moment
if jcost['count'] == 1:
cost = jcost['results'][0][u'value_inc_vat']
price = price + cost * (results[curridx][u'consumption'])
else:
print('Unmatched consumption {}'.format(
results[curridx][u'interval_start']) +
' / cost {}'.format(cost[curridx][u'valid_from']))
price = price + ((cost[curridx][u'value_inc_vat']) *
(results[curridx][u'consumption']))
print('Error: can only process fixed price gas')
price = 0

return usage, price

0 comments on commit 3c79fda

Please sign in to comment.