mirror of
https://github.com/mlesniew/elicznik.git
synced 2025-12-31 10:07:26 +01:00
Allow using CSV and Chart API
This commit is contained in:
committed by
Michał Leśniewski
parent
3c5ce038a6
commit
30b39739a6
39
README.md
39
README.md
@@ -17,22 +17,23 @@ $ pip3 install elicznik
|
||||
|
||||
With the package installed readings can be retrieved by simply running the `elicznik` command:
|
||||
```
|
||||
usage: elicznik [-h] [--format {raw,table,csv}] username password [start date] [end date]
|
||||
usage: elicznik [-h] [--format {table,csv}] [--api {chart,csv}] username password [start_date] [end_date]
|
||||
|
||||
positional arguments:
|
||||
username tauron-dystrybucja.pl user name
|
||||
password tauron-dystrybucja.pl password
|
||||
start date Start date of date range to be retrieved, in ISO8601 format. If the end date is omitted, it's the only date for which
|
||||
start_date Start date of date range to be retrieved, in ISO8601 format. If the end date is omitted, it's the only date for which
|
||||
measurements are retrieved.
|
||||
end date End date of date range to be retrieved, inclusive, in ISO8601 format. Can be omitted to only retrieve a single day's
|
||||
end_date End date of date range to be retrieved, inclusive, in ISO8601 format. Can be omitted to only retrieve a single day's
|
||||
measurements.
|
||||
|
||||
optional arguments:
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--format {raw,table,csv}
|
||||
Specify the output format
|
||||
--format {table,csv} Specify the output format
|
||||
--api {chart,csv} Specify which Tauron API to use to get the measurements.
|
||||
```
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
@@ -92,6 +93,32 @@ with elicznik.ELicznik("freddy@example.com", "secretpassword") as m:
|
||||
```
|
||||
|
||||
|
||||
## Notes on APIs and the `--api` command line switch
|
||||
|
||||
Tauron exposes two API endpoints for retrieving meter readings -- one for downloading CSV (and XLS) data,
|
||||
the other is a back-end supporting the charts in the Web UI. In theory, both endpoints are equivalent.
|
||||
They can be used to get exactly the same data. In practice, the endpoint for downloading CSV data seems
|
||||
more stable -- in contrast to the chart one, which changed a few times in the past. The CSV endpoint is
|
||||
also more robust and allows downloading more data with fewer requests.
|
||||
|
||||
This project supports fetching data from both. CSV is the default and recommended one, but it's possible
|
||||
to switch to the chart API endpoint in case of problems.
|
||||
|
||||
This can be done by adding `--api=chart` on the command line:
|
||||
```
|
||||
$ elicznik --api=chart freddy@example.com secretpassword 2021-07-10
|
||||
```
|
||||
|
||||
Both APIs can also be used explicitly from code. The `elicznik` module defines two classes for that `ELicznikChart`
|
||||
and `ELicznikCSV`. `ELicznik` is just an alias for `ELicznikCSV`:
|
||||
```
|
||||
import elicznik
|
||||
|
||||
with elicznik.ELicznikChart("freddy@example.com", "secretpassword") as m:
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## TODO & bugs
|
||||
|
||||
* Add support for accounts with multiple meters
|
||||
|
||||
@@ -1 +1 @@
|
||||
from .elicznik import ELicznik
|
||||
from .elicznik import ELicznik, ELicznikChart, ELicznikCSV
|
||||
|
||||
@@ -5,7 +5,7 @@ import sys
|
||||
|
||||
import tabulate
|
||||
|
||||
from .elicznik import ELicznik
|
||||
from .elicznik import ELicznikChart, ELicznikCSV
|
||||
|
||||
|
||||
def main():
|
||||
@@ -16,6 +16,12 @@ def main():
|
||||
default="table",
|
||||
help="Specify the output format",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--api",
|
||||
choices=["chart", "csv"],
|
||||
default="csv",
|
||||
help="Specify which Tauron API to use to get the measurements. "
|
||||
)
|
||||
parser.add_argument("username", help="tauron-dystrybucja.pl user name")
|
||||
parser.add_argument("password", help="tauron-dystrybucja.pl password")
|
||||
parser.add_argument(
|
||||
@@ -39,7 +45,9 @@ def main():
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
with ELicznik(args.username, args.password) as elicznik:
|
||||
elicznik_class = ELicznikCSV if args.api == "csv" else ELicznikChart
|
||||
|
||||
with elicznik_class(args.username, args.password) as elicznik:
|
||||
result = elicznik.get_readings(args.start_date, args.end_date)
|
||||
|
||||
if args.format == "table":
|
||||
|
||||
@@ -5,10 +5,8 @@ import datetime
|
||||
|
||||
from .session import Session
|
||||
|
||||
|
||||
class ELicznik:
|
||||
class ELicznikBase:
|
||||
LOGIN_URL = "https://logowanie.tauron-dystrybucja.pl/login"
|
||||
DATA_URL = "https://elicznik.tauron-dystrybucja.pl/energia/do/dane"
|
||||
|
||||
def __init__(self, username, password):
|
||||
self.username = username
|
||||
@@ -33,7 +31,48 @@ class ELicznik:
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
pass
|
||||
|
||||
def get_raw_data(self, start_date, end_date=None):
|
||||
|
||||
class ELicznikChart(ELicznikBase):
|
||||
CHART_URL = "https://elicznik.tauron-dystrybucja.pl/energia/api"
|
||||
|
||||
def _get_raw_daily_readings(self, type_, date):
|
||||
data = self.session.post(
|
||||
self.CHART_URL,
|
||||
data={
|
||||
"type": type_,
|
||||
"from": date.strftime("%d.%m.%Y"),
|
||||
"to": date.strftime("%d.%m.%Y"),
|
||||
"profile": "full time",
|
||||
},
|
||||
).json().get("data", {}).get("values", [])
|
||||
|
||||
return ((datetime.datetime.combine(date, datetime.time(h)), value) for h, value in enumerate(data))
|
||||
|
||||
def _get_raw_readings(self, type_, start_date, end_date=None):
|
||||
end_date = end_date or start_date
|
||||
while start_date <= end_date:
|
||||
yield from self._get_raw_daily_readings(type_, start_date)
|
||||
start_date += datetime.timedelta(days=1)
|
||||
|
||||
def get_readings_production(self, start_date, end_date=None):
|
||||
return dict(self._get_raw_readings("oze", start_date, end_date))
|
||||
|
||||
def get_readings_consumption(self, start_date, end_date=None):
|
||||
return dict(self._get_raw_readings("consum", start_date, end_date))
|
||||
|
||||
def get_readings(self, start_date, end_date=None):
|
||||
consumed = self.get_readings_consumption(start_date, end_date)
|
||||
produced = self.get_readings_production(start_date, end_date)
|
||||
return sorted(
|
||||
(timestamp, consumed.get(timestamp), produced.get(timestamp))
|
||||
for timestamp in set(consumed) | set(produced)
|
||||
)
|
||||
|
||||
|
||||
class ELicznikCSV(ELicznikBase):
|
||||
DATA_URL = "https://elicznik.tauron-dystrybucja.pl/energia/do/dane"
|
||||
|
||||
def _get_raw_data(self, start_date, end_date=None):
|
||||
end_date = end_date or start_date
|
||||
return self.session.post(
|
||||
self.DATA_URL,
|
||||
@@ -48,20 +87,18 @@ class ELicznik:
|
||||
).text.splitlines()
|
||||
|
||||
@staticmethod
|
||||
def parse_timestamp(timespec):
|
||||
def _parse_timestamp(timespec):
|
||||
date, time = timespec.split(None, 1)
|
||||
hour = int(time.split(":")[0]) - 1
|
||||
return datetime.datetime.strptime(date, "%Y-%m-%d") + datetime.timedelta(
|
||||
hours=hour
|
||||
)
|
||||
return datetime.datetime.strptime(date, "%Y-%m-%d") + datetime.timedelta(hours=hour)
|
||||
|
||||
def get_readings(self, start_date, end_date=None):
|
||||
end_date = end_date or start_date
|
||||
data = self.get_raw_data(start_date, end_date)
|
||||
data = self._get_raw_data(start_date, end_date)
|
||||
|
||||
records = [
|
||||
{
|
||||
"timestamp": self.parse_timestamp(rec["Data"]),
|
||||
"timestamp": self._parse_timestamp(rec["Data"]),
|
||||
"value": float(rec[" Wartość kWh"].replace(",", ".")),
|
||||
"type": rec["Rodzaj"],
|
||||
}
|
||||
@@ -75,12 +112,14 @@ class ELicznik:
|
||||
]
|
||||
|
||||
prod = {
|
||||
rec["timestamp"]: rec["value"] for rec in records if rec["type"] == "pobór"
|
||||
rec["timestamp"]: rec["value"]
|
||||
for rec in records
|
||||
if rec["type"] == "oddanie"
|
||||
}
|
||||
cons = {
|
||||
rec["timestamp"]: rec["value"]
|
||||
for rec in records
|
||||
if rec["type"] == "oddanie"
|
||||
if rec["type"] == "pobór"
|
||||
}
|
||||
|
||||
# TODO
|
||||
@@ -90,3 +129,5 @@ class ELicznik:
|
||||
(timestamp, cons.get(timestamp), prod.get(timestamp))
|
||||
for timestamp in set(cons) | set(prod)
|
||||
)
|
||||
|
||||
ELicznik = ELicznikCSV
|
||||
|
||||
Reference in New Issue
Block a user