e-data 1.2.18__tar.gz → 1.2.19__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- e_data-1.2.19/PKG-INFO +136 -0
- e_data-1.2.19/e_data.egg-info/PKG-INFO +136 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/connectors/datadis.py +13 -9
- {e-data-1.2.18 → e_data-1.2.19}/edata/definitions.py +1 -3
- {e-data-1.2.18 → e_data-1.2.19}/edata/helpers.py +63 -67
- {e-data-1.2.18 → e_data-1.2.19}/setup.py +1 -1
- e-data-1.2.18/PKG-INFO +0 -128
- e-data-1.2.18/e_data.egg-info/PKG-INFO +0 -128
- {e-data-1.2.18 → e_data-1.2.19}/LICENSE +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/MANIFEST.in +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/README.md +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/e_data.egg-info/SOURCES.txt +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/e_data.egg-info/dependency_links.txt +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/e_data.egg-info/requires.txt +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/e_data.egg-info/top_level.txt +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/__init__.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/connectors/__init__.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/connectors/redata.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/const.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/processors/__init__.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/processors/base.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/processors/billing.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/processors/consumption.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/processors/maximeter.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/processors/utils.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/edata/storage.py +0 -0
- {e-data-1.2.18 → e_data-1.2.19}/setup.cfg +0 -0
e_data-1.2.19/PKG-INFO
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: e-data
|
|
3
|
+
Version: 1.2.19
|
|
4
|
+
Summary: Python library for managing spanish energy data from various web providers
|
|
5
|
+
Home-page: https://github.com/uvejota/python-edata
|
|
6
|
+
Author: VMG
|
|
7
|
+
Author-email: vmayorg@outlook.es
|
|
8
|
+
License: GPLv3
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
18
|
+
Requires-Python: >=3.6.0
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: dateparser>=1.1.2
|
|
22
|
+
Requires-Dist: freezegun>=1.2.1
|
|
23
|
+
Requires-Dist: holidays>=0.14.2
|
|
24
|
+
Requires-Dist: pytest>=7.1.2
|
|
25
|
+
Requires-Dist: python_dateutil>=2.8.2
|
|
26
|
+
Requires-Dist: requests>=2.28.1
|
|
27
|
+
Requires-Dist: voluptuous>=0.13.1
|
|
28
|
+
Requires-Dist: Jinja2>=3.1.2
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
[](https://pepy.tech/project/e-data)
|
|
32
|
+
[](https://pepy.tech/project/e-data)
|
|
33
|
+
[](https://pepy.tech/project/e-data)
|
|
34
|
+
|
|
35
|
+
# python-edata
|
|
36
|
+
|
|
37
|
+
Este paquete proporciona herramientas para la descarga de tus datos de consumo eléctrico (desde Datadis.es) y su posterior procesado. La motivación principal es que conocer el consumo puede ayudarnos a reducirlo, e incluso a elegir una tarifa que mejor se adapte a nuestras necesidades. A día de hoy sus capacidades de facturación (€) son limitadas, soporta PVPC (según disponibilidad de datos de REData) y tarificación fija por tramos. Es el corazón de la integración [homeassistant-edata](https://github.com/uvejota/homeassistant-edata).
|
|
38
|
+
|
|
39
|
+
_**Esta herramienta no mantiene ningún tipo de vinculación con los proveedores de datos anteriormente mencionados, simplemente consulta la información disponible y facilita su posterior análisis.**_
|
|
40
|
+
|
|
41
|
+
## Instalación
|
|
42
|
+
|
|
43
|
+
Puedes instalar la última versión estable mediante:
|
|
44
|
+
|
|
45
|
+
``` bash
|
|
46
|
+
pip install e-data
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Si quieres probar la versión `dev` o contribuir a su desarrollo, clona este repositorio e instala manualmente las dependencias:
|
|
50
|
+
|
|
51
|
+
``` bash
|
|
52
|
+
pip install -r requirements.txt
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Estructura
|
|
56
|
+
|
|
57
|
+
El paquete consta de tres módulos diferenciados:
|
|
58
|
+
|
|
59
|
+
* **Conectores** (módulo `connectors`), para definir los métodos de consulta a los diferentes proveedores: Datadis y REData.
|
|
60
|
+
* **Procesadores** (módulo `processors`), para procesar datos de consumo, maxímetro, o coste (tarificación). Ahora mismo consta de tres procesadores: `billing`, `consumption` y `maximeter`, además de algunas utilidades ubicadas en `utils`. Los procesadores deben heredar de la clase Processor definida en `base.py`
|
|
61
|
+
* **Ayudantes** (módulo `helpers`), para ayudar en el uso y gestión de los anteriores, presentando de momento un único ayudante llamado `EdataHelper` que te permite recopilar `X` días de datos (por defecto 365) y automáticamente procesarlos. Los datos son almacenados en la variable `data`, mientras que los atributos autocalculados son almacenados en la variable `attributes`. Por lo general, primero utilizan los conectores y luego procesan los datos, gestionando varias tareas de recuperación (principalmente para Datadis).
|
|
62
|
+
|
|
63
|
+
Estos módulos corresponden a la siguiente estructura del paquete:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
edata/
|
|
67
|
+
· __init__.py
|
|
68
|
+
· connectors/
|
|
69
|
+
· __init__.py
|
|
70
|
+
· datadis.py
|
|
71
|
+
· redata.py
|
|
72
|
+
· processors/
|
|
73
|
+
· __init__.py
|
|
74
|
+
· base.py
|
|
75
|
+
· billing.py
|
|
76
|
+
· consumption.py
|
|
77
|
+
· maximeter.py
|
|
78
|
+
· utils.py
|
|
79
|
+
· helpers.py
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Ejemplo de uso
|
|
83
|
+
|
|
84
|
+
Partimos de que tenemos credenciales en Datadis.es. Algunas aclaraciones:
|
|
85
|
+
* No es necesario solicitar API pública en el registro (se utilizará la API privada habilitada por defecto)
|
|
86
|
+
* El username suele ser el NIF del titular
|
|
87
|
+
* Copie el CUPS de la web de Datadis, algunas comercializadoras adhieren caracteres adicionales en el CUPS mostrado en su factura.
|
|
88
|
+
* La herramienta acepta el uso de NIF autorizado para consultar el suministro de otro titular.
|
|
89
|
+
|
|
90
|
+
``` python
|
|
91
|
+
from datetime import datetime
|
|
92
|
+
import json
|
|
93
|
+
|
|
94
|
+
# importamos definiciones de datos que nos interesen
|
|
95
|
+
from edata.definitions import PricingRules
|
|
96
|
+
# importamos el ayudante
|
|
97
|
+
from edata.helpers import EdataHelper
|
|
98
|
+
# importamos el procesador de utilidades
|
|
99
|
+
from edata.processors import utils
|
|
100
|
+
|
|
101
|
+
# Preparar reglas de tarificación (si se quiere)
|
|
102
|
+
PRICING_RULES_PVPC = PricingRules(
|
|
103
|
+
p1_kw_year_eur=30.67266,
|
|
104
|
+
p2_kw_year_eur=1.4243591,
|
|
105
|
+
meter_month_eur=0.81,
|
|
106
|
+
market_kw_year_eur=3.113,
|
|
107
|
+
electricity_tax=1.0511300560,
|
|
108
|
+
iva_tax=1.05,
|
|
109
|
+
# podemos rellenar los siguientes campos si quisiéramos precio fijo (y no pvpc)
|
|
110
|
+
p1_kwh_eur=None,
|
|
111
|
+
p2_kwh_eur=None,
|
|
112
|
+
p3_kwh_eur=None,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Instanciar el helper
|
|
116
|
+
# 'authorized_nif' permite indicar el NIF de la persona que nos autoriza a consultar su CUPS.
|
|
117
|
+
# 'data' permite "cargar" al helper datos anteriores (resultado edata.data de una ejecución anterior), para evitar volver a consultar los mismos.
|
|
118
|
+
edata = EdataHelper(
|
|
119
|
+
"datadis_user",
|
|
120
|
+
"datadis_password",
|
|
121
|
+
"cups",
|
|
122
|
+
datadis_authorized_nif=None,
|
|
123
|
+
pricing_rules=PRICING_RULES_PVPC, # si se le pasa None, no aplica tarificación
|
|
124
|
+
data=None, # aquí podríamos cargar datos anteriores
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Solicitar actualización de todo el histórico (se almacena en edata.data)
|
|
128
|
+
edata.update(date_from=datetime(1970, 1, 1), date_to=datetime.today())
|
|
129
|
+
|
|
130
|
+
# volcamos todo lo obtenido a un fichero
|
|
131
|
+
with open("backup.json", "w") as file:
|
|
132
|
+
json.dump(utils.serialize_dict(edata.data), file) # se puede utilizar deserialize_dict para la posterior lectura del backup
|
|
133
|
+
|
|
134
|
+
# Imprimir atributos
|
|
135
|
+
print(edata.attributes)
|
|
136
|
+
```
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: e-data
|
|
3
|
+
Version: 1.2.19
|
|
4
|
+
Summary: Python library for managing spanish energy data from various web providers
|
|
5
|
+
Home-page: https://github.com/uvejota/python-edata
|
|
6
|
+
Author: VMG
|
|
7
|
+
Author-email: vmayorg@outlook.es
|
|
8
|
+
License: GPLv3
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
18
|
+
Requires-Python: >=3.6.0
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: dateparser>=1.1.2
|
|
22
|
+
Requires-Dist: freezegun>=1.2.1
|
|
23
|
+
Requires-Dist: holidays>=0.14.2
|
|
24
|
+
Requires-Dist: pytest>=7.1.2
|
|
25
|
+
Requires-Dist: python_dateutil>=2.8.2
|
|
26
|
+
Requires-Dist: requests>=2.28.1
|
|
27
|
+
Requires-Dist: voluptuous>=0.13.1
|
|
28
|
+
Requires-Dist: Jinja2>=3.1.2
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
[](https://pepy.tech/project/e-data)
|
|
32
|
+
[](https://pepy.tech/project/e-data)
|
|
33
|
+
[](https://pepy.tech/project/e-data)
|
|
34
|
+
|
|
35
|
+
# python-edata
|
|
36
|
+
|
|
37
|
+
Este paquete proporciona herramientas para la descarga de tus datos de consumo eléctrico (desde Datadis.es) y su posterior procesado. La motivación principal es que conocer el consumo puede ayudarnos a reducirlo, e incluso a elegir una tarifa que mejor se adapte a nuestras necesidades. A día de hoy sus capacidades de facturación (€) son limitadas, soporta PVPC (según disponibilidad de datos de REData) y tarificación fija por tramos. Es el corazón de la integración [homeassistant-edata](https://github.com/uvejota/homeassistant-edata).
|
|
38
|
+
|
|
39
|
+
_**Esta herramienta no mantiene ningún tipo de vinculación con los proveedores de datos anteriormente mencionados, simplemente consulta la información disponible y facilita su posterior análisis.**_
|
|
40
|
+
|
|
41
|
+
## Instalación
|
|
42
|
+
|
|
43
|
+
Puedes instalar la última versión estable mediante:
|
|
44
|
+
|
|
45
|
+
``` bash
|
|
46
|
+
pip install e-data
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Si quieres probar la versión `dev` o contribuir a su desarrollo, clona este repositorio e instala manualmente las dependencias:
|
|
50
|
+
|
|
51
|
+
``` bash
|
|
52
|
+
pip install -r requirements.txt
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Estructura
|
|
56
|
+
|
|
57
|
+
El paquete consta de tres módulos diferenciados:
|
|
58
|
+
|
|
59
|
+
* **Conectores** (módulo `connectors`), para definir los métodos de consulta a los diferentes proveedores: Datadis y REData.
|
|
60
|
+
* **Procesadores** (módulo `processors`), para procesar datos de consumo, maxímetro, o coste (tarificación). Ahora mismo consta de tres procesadores: `billing`, `consumption` y `maximeter`, además de algunas utilidades ubicadas en `utils`. Los procesadores deben heredar de la clase Processor definida en `base.py`
|
|
61
|
+
* **Ayudantes** (módulo `helpers`), para ayudar en el uso y gestión de los anteriores, presentando de momento un único ayudante llamado `EdataHelper` que te permite recopilar `X` días de datos (por defecto 365) y automáticamente procesarlos. Los datos son almacenados en la variable `data`, mientras que los atributos autocalculados son almacenados en la variable `attributes`. Por lo general, primero utilizan los conectores y luego procesan los datos, gestionando varias tareas de recuperación (principalmente para Datadis).
|
|
62
|
+
|
|
63
|
+
Estos módulos corresponden a la siguiente estructura del paquete:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
edata/
|
|
67
|
+
· __init__.py
|
|
68
|
+
· connectors/
|
|
69
|
+
· __init__.py
|
|
70
|
+
· datadis.py
|
|
71
|
+
· redata.py
|
|
72
|
+
· processors/
|
|
73
|
+
· __init__.py
|
|
74
|
+
· base.py
|
|
75
|
+
· billing.py
|
|
76
|
+
· consumption.py
|
|
77
|
+
· maximeter.py
|
|
78
|
+
· utils.py
|
|
79
|
+
· helpers.py
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Ejemplo de uso
|
|
83
|
+
|
|
84
|
+
Partimos de que tenemos credenciales en Datadis.es. Algunas aclaraciones:
|
|
85
|
+
* No es necesario solicitar API pública en el registro (se utilizará la API privada habilitada por defecto)
|
|
86
|
+
* El username suele ser el NIF del titular
|
|
87
|
+
* Copie el CUPS de la web de Datadis, algunas comercializadoras adhieren caracteres adicionales en el CUPS mostrado en su factura.
|
|
88
|
+
* La herramienta acepta el uso de NIF autorizado para consultar el suministro de otro titular.
|
|
89
|
+
|
|
90
|
+
``` python
|
|
91
|
+
from datetime import datetime
|
|
92
|
+
import json
|
|
93
|
+
|
|
94
|
+
# importamos definiciones de datos que nos interesen
|
|
95
|
+
from edata.definitions import PricingRules
|
|
96
|
+
# importamos el ayudante
|
|
97
|
+
from edata.helpers import EdataHelper
|
|
98
|
+
# importamos el procesador de utilidades
|
|
99
|
+
from edata.processors import utils
|
|
100
|
+
|
|
101
|
+
# Preparar reglas de tarificación (si se quiere)
|
|
102
|
+
PRICING_RULES_PVPC = PricingRules(
|
|
103
|
+
p1_kw_year_eur=30.67266,
|
|
104
|
+
p2_kw_year_eur=1.4243591,
|
|
105
|
+
meter_month_eur=0.81,
|
|
106
|
+
market_kw_year_eur=3.113,
|
|
107
|
+
electricity_tax=1.0511300560,
|
|
108
|
+
iva_tax=1.05,
|
|
109
|
+
# podemos rellenar los siguientes campos si quisiéramos precio fijo (y no pvpc)
|
|
110
|
+
p1_kwh_eur=None,
|
|
111
|
+
p2_kwh_eur=None,
|
|
112
|
+
p3_kwh_eur=None,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Instanciar el helper
|
|
116
|
+
# 'authorized_nif' permite indicar el NIF de la persona que nos autoriza a consultar su CUPS.
|
|
117
|
+
# 'data' permite "cargar" al helper datos anteriores (resultado edata.data de una ejecución anterior), para evitar volver a consultar los mismos.
|
|
118
|
+
edata = EdataHelper(
|
|
119
|
+
"datadis_user",
|
|
120
|
+
"datadis_password",
|
|
121
|
+
"cups",
|
|
122
|
+
datadis_authorized_nif=None,
|
|
123
|
+
pricing_rules=PRICING_RULES_PVPC, # si se le pasa None, no aplica tarificación
|
|
124
|
+
data=None, # aquí podríamos cargar datos anteriores
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Solicitar actualización de todo el histórico (se almacena en edata.data)
|
|
128
|
+
edata.update(date_from=datetime(1970, 1, 1), date_to=datetime.today())
|
|
129
|
+
|
|
130
|
+
# volcamos todo lo obtenido a un fichero
|
|
131
|
+
with open("backup.json", "w") as file:
|
|
132
|
+
json.dump(utils.serialize_dict(edata.data), file) # se puede utilizar deserialize_dict para la posterior lectura del backup
|
|
133
|
+
|
|
134
|
+
# Imprimir atributos
|
|
135
|
+
print(edata.attributes)
|
|
136
|
+
```
|
|
@@ -7,16 +7,15 @@ There a few issues that are workarounded:
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import contextlib
|
|
10
|
+
from datetime import datetime, timedelta
|
|
10
11
|
import hashlib
|
|
11
12
|
import json
|
|
12
13
|
import logging
|
|
13
|
-
from datetime import datetime, timedelta
|
|
14
14
|
import os
|
|
15
|
-
from dateutil.relativedelta import relativedelta
|
|
16
|
-
|
|
17
|
-
import requests
|
|
18
15
|
|
|
16
|
+
from dateutil.relativedelta import relativedelta
|
|
19
17
|
from filelock import FileLock
|
|
18
|
+
import requests
|
|
20
19
|
|
|
21
20
|
from ..definitions import ConsumptionData, ContractData, MaxPowerData, SupplyData
|
|
22
21
|
from ..processors import utils
|
|
@@ -155,7 +154,8 @@ class DatadisConnector:
|
|
|
155
154
|
self._recent_queries_cache_file, "w", encoding="utf8"
|
|
156
155
|
) as dst_file:
|
|
157
156
|
json.dump(self._recent_cache, dst_file)
|
|
158
|
-
_LOGGER.
|
|
157
|
+
_LOGGER.debug("Local cache updated")
|
|
158
|
+
|
|
159
159
|
except Exception as e:
|
|
160
160
|
_LOGGER.warning("Unknown error while updating cache: %s", e)
|
|
161
161
|
|
|
@@ -253,13 +253,15 @@ class DatadisConnector:
|
|
|
253
253
|
_LOGGER.info("Got 200 OK at %s", url + params)
|
|
254
254
|
if reply.json():
|
|
255
255
|
response = reply.json()
|
|
256
|
-
|
|
256
|
+
if not ignore_recent_queries:
|
|
257
|
+
self._update_recent_queries(url + params, response)
|
|
257
258
|
else:
|
|
258
259
|
# this mostly happens when datadis provides an empty response
|
|
259
260
|
_LOGGER.info(
|
|
260
261
|
"Datadis returned an empty response at %s", url + params
|
|
261
262
|
)
|
|
262
|
-
|
|
263
|
+
if not ignore_recent_queries:
|
|
264
|
+
self._update_recent_queries(url + params)
|
|
263
265
|
elif reply.status_code == 401 and not refresh_token:
|
|
264
266
|
# we're here if we were unauthorized so we will refresh the token
|
|
265
267
|
response = self._get(
|
|
@@ -276,7 +278,8 @@ class DatadisConnector:
|
|
|
276
278
|
reply.text,
|
|
277
279
|
url + params,
|
|
278
280
|
)
|
|
279
|
-
|
|
281
|
+
if not ignore_recent_queries:
|
|
282
|
+
self._update_recent_queries(url + params)
|
|
280
283
|
elif is_retry:
|
|
281
284
|
# otherwise, if this was a retried request... warn the user
|
|
282
285
|
if (url + params) not in self._warned_queries:
|
|
@@ -288,7 +291,8 @@ class DatadisConnector:
|
|
|
288
291
|
"Query temporary disabled",
|
|
289
292
|
"Future 500 code errors for this query will be silenced until restart",
|
|
290
293
|
)
|
|
291
|
-
|
|
294
|
+
if not ignore_recent_queries:
|
|
295
|
+
self._update_recent_queries(url + params)
|
|
292
296
|
self._warned_queries.append(url + params)
|
|
293
297
|
else:
|
|
294
298
|
# finally, retry since an unexpected error took place (mostly 500 errors - server fault)
|
|
@@ -71,9 +71,7 @@ DEFAULT_BILLING_SURPLUS_FORMULA = (
|
|
|
71
71
|
|
|
72
72
|
# Sum energy and power terms, and substract surplus until 0.
|
|
73
73
|
# An alternative would be "[(energy_term + power_term - surplus_term), 0]|max + others_term"
|
|
74
|
-
DEFAULT_BILLING_MAIN_FORMULA =
|
|
75
|
-
"[(energy_term - surplus_term), 0]|max + power_term + others_term"
|
|
76
|
-
)
|
|
74
|
+
DEFAULT_BILLING_MAIN_FORMULA = "energy_term + power_term + others_term"
|
|
77
75
|
|
|
78
76
|
|
|
79
77
|
class SupplyData(TypedDict):
|
|
@@ -22,6 +22,12 @@ from .storage import check_storage_integrity, dump_storage, load_storage
|
|
|
22
22
|
_LOGGER = logging.getLogger(__name__)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
def acups(cups):
|
|
26
|
+
"""Print an abbreviated and anonymized CUPS."""
|
|
27
|
+
|
|
28
|
+
return cups[-5:]
|
|
29
|
+
|
|
30
|
+
|
|
25
31
|
class EdataHelper:
|
|
26
32
|
"""Main EdataHelper class."""
|
|
27
33
|
|
|
@@ -53,7 +59,7 @@ class EdataHelper:
|
|
|
53
59
|
self.attributes = {}
|
|
54
60
|
self._storage_dir = storage_dir_path
|
|
55
61
|
self._cups = cups
|
|
56
|
-
self._scups = cups
|
|
62
|
+
self._scups = acups(cups)
|
|
57
63
|
self._authorized_nif = datadis_authorized_nif
|
|
58
64
|
self.last_update = {x: datetime(1970, 1, 1) for x in self.data}
|
|
59
65
|
self._date_from = datetime(1970, 1, 1)
|
|
@@ -113,7 +119,7 @@ class EdataHelper:
|
|
|
113
119
|
date_to: datetime = datetime.today(),
|
|
114
120
|
incremental_update: bool = True,
|
|
115
121
|
):
|
|
116
|
-
"""
|
|
122
|
+
"""Update synchronously."""
|
|
117
123
|
|
|
118
124
|
_LOGGER.info(
|
|
119
125
|
"%s: update triggered",
|
|
@@ -138,9 +144,9 @@ class EdataHelper:
|
|
|
138
144
|
dump_storage(self._cups, self.data, self._storage_dir)
|
|
139
145
|
|
|
140
146
|
def update_supplies(self):
|
|
141
|
-
"""
|
|
147
|
+
"""Update supplies."""
|
|
142
148
|
|
|
143
|
-
_LOGGER.
|
|
149
|
+
_LOGGER.debug("%s: supplies update triggered", self._scups)
|
|
144
150
|
if datetime.today().date() != self.last_update["supplies"].date():
|
|
145
151
|
# if supplies haven't been updated today
|
|
146
152
|
supplies = self.datadis_api.get_supplies(
|
|
@@ -155,9 +161,9 @@ class EdataHelper:
|
|
|
155
161
|
_LOGGER.info("%s: supplies are already updated (skipping)", self._scups)
|
|
156
162
|
|
|
157
163
|
def update_contracts(self, cups: str, distributor_code: str):
|
|
158
|
-
"""
|
|
164
|
+
"""Update contracts."""
|
|
159
165
|
|
|
160
|
-
_LOGGER.
|
|
166
|
+
_LOGGER.debug("%s: contracts update triggered", self._scups)
|
|
161
167
|
if datetime.today().date() != self.last_update["contracts"].date():
|
|
162
168
|
# if contracts haven't been updated today
|
|
163
169
|
contracts = self.datadis_api.get_contract_detail(
|
|
@@ -182,9 +188,9 @@ class EdataHelper:
|
|
|
182
188
|
measurement_type: str,
|
|
183
189
|
point_type: int,
|
|
184
190
|
):
|
|
185
|
-
"""
|
|
191
|
+
"""Update consumptions."""
|
|
186
192
|
|
|
187
|
-
_LOGGER.
|
|
193
|
+
_LOGGER.debug("%s: consumptions update triggered", self._scups)
|
|
188
194
|
if (datetime.now() - self.last_update["consumptions"]) > self.UPDATE_INTERVAL:
|
|
189
195
|
consumptions = self.datadis_api.get_consumption_data(
|
|
190
196
|
cups,
|
|
@@ -196,22 +202,23 @@ class EdataHelper:
|
|
|
196
202
|
authorized_nif=self._authorized_nif,
|
|
197
203
|
)
|
|
198
204
|
if len(consumptions) > 0:
|
|
205
|
+
_LOGGER.info(
|
|
206
|
+
"%s: got consumptions from %s to %s",
|
|
207
|
+
self._scups,
|
|
208
|
+
consumptions[0]["datetime"].isoformat(),
|
|
209
|
+
consumptions[-1]["datetime"].isoformat(),
|
|
210
|
+
)
|
|
199
211
|
self.data["consumptions"] = utils.extend_by_key(
|
|
200
212
|
self.data["consumptions"], consumptions, "datetime"
|
|
201
213
|
)
|
|
202
214
|
self.last_update["consumptions"] = datetime.now()
|
|
203
|
-
_LOGGER.info(
|
|
204
|
-
"%s: consumptions update succeeded (%s new records)",
|
|
205
|
-
self._scups,
|
|
206
|
-
len(consumptions),
|
|
207
|
-
)
|
|
208
215
|
else:
|
|
209
216
|
_LOGGER.info("%s: consumptions are already updated (skipping)", self._scups)
|
|
210
217
|
|
|
211
218
|
def update_maximeter(self, cups, distributor_code, start_date, end_date):
|
|
212
|
-
"""
|
|
219
|
+
"""Update maximeter."""
|
|
213
220
|
|
|
214
|
-
_LOGGER.
|
|
221
|
+
_LOGGER.debug("%s: maximeter update triggered", self._scups)
|
|
215
222
|
if (datetime.now() - self.last_update["maximeter"]) > self.UPDATE_INTERVAL:
|
|
216
223
|
maximeter = self.datadis_api.get_max_power(
|
|
217
224
|
cups,
|
|
@@ -221,15 +228,14 @@ class EdataHelper:
|
|
|
221
228
|
authorized_nif=self._authorized_nif,
|
|
222
229
|
)
|
|
223
230
|
if len(maximeter) > 0:
|
|
231
|
+
_LOGGER.info(
|
|
232
|
+
"%s: maximeter update succeeded",
|
|
233
|
+
self._scups,
|
|
234
|
+
)
|
|
224
235
|
self.data["maximeter"] = utils.extend_by_key(
|
|
225
236
|
self.data["maximeter"], maximeter, "datetime"
|
|
226
237
|
)
|
|
227
238
|
self.last_update["maximeter"] = datetime.now()
|
|
228
|
-
_LOGGER.info(
|
|
229
|
-
"%s: maximeter update succeeded (%s new records)",
|
|
230
|
-
self._scups,
|
|
231
|
-
len(maximeter),
|
|
232
|
-
)
|
|
233
239
|
else:
|
|
234
240
|
_LOGGER.info("%s: maximeter is already updated (skipping)", self._scups)
|
|
235
241
|
|
|
@@ -239,7 +245,7 @@ class EdataHelper:
|
|
|
239
245
|
date_from: datetime = datetime(1970, 1, 1),
|
|
240
246
|
date_to: datetime = datetime.today(),
|
|
241
247
|
):
|
|
242
|
-
"""
|
|
248
|
+
"""Update all data from Datadis."""
|
|
243
249
|
|
|
244
250
|
_LOGGER.info(
|
|
245
251
|
"%s: datadis update triggered (from %s to %s)",
|
|
@@ -264,15 +270,22 @@ class EdataHelper:
|
|
|
264
270
|
if supply is None:
|
|
265
271
|
# return if specified cups seems not valid
|
|
266
272
|
_LOGGER.error(
|
|
267
|
-
"%s: CUPS not found
|
|
273
|
+
"%s: CUPS not found. Got: %s",
|
|
274
|
+
self._scups,
|
|
275
|
+
[acups(x["cups"]) for x in self.data["supplies"]],
|
|
268
276
|
)
|
|
269
277
|
return False
|
|
278
|
+
_LOGGER.info("%s: CUPS found in account", self._scups)
|
|
270
279
|
|
|
271
280
|
# get some supply-related data
|
|
272
281
|
supply_date_start = supply["date_start"]
|
|
273
282
|
distributor_code = supply["distributorCode"]
|
|
274
283
|
point_type = supply["pointType"]
|
|
275
284
|
|
|
285
|
+
_LOGGER.info(
|
|
286
|
+
"%s: CUPS start date is %s", self._scups, supply_date_start.isoformat()
|
|
287
|
+
)
|
|
288
|
+
|
|
276
289
|
# update contracts to get valid periods
|
|
277
290
|
self.update_contracts(cups, distributor_code)
|
|
278
291
|
if len(self.data["contracts"]) == 0:
|
|
@@ -282,7 +295,7 @@ class EdataHelper:
|
|
|
282
295
|
)
|
|
283
296
|
return False
|
|
284
297
|
|
|
285
|
-
# filter consumptions and maximeter, and
|
|
298
|
+
# filter consumptions and maximeter, and log gaps
|
|
286
299
|
def sort_and_filter(dt_from, dt_to):
|
|
287
300
|
self.data["consumptions"], miss_cons = utils.extract_dt_ranges(
|
|
288
301
|
self.data["consumptions"],
|
|
@@ -318,64 +331,47 @@ class EdataHelper:
|
|
|
318
331
|
),
|
|
319
332
|
)
|
|
320
333
|
|
|
321
|
-
|
|
322
|
-
for
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
# update consumptions
|
|
328
|
-
for gap in [
|
|
329
|
-
x
|
|
330
|
-
for x in miss_cons
|
|
331
|
-
if not (
|
|
332
|
-
x["to"] < contract["date_start"] or x["from"] > contract["date_end"]
|
|
333
|
-
)
|
|
334
|
-
]:
|
|
334
|
+
# update consumptions
|
|
335
|
+
for gap in miss_cons:
|
|
336
|
+
if not (
|
|
337
|
+
gap["to"] < supply["date_start"] or gap["from"] > supply["date_end"]
|
|
338
|
+
):
|
|
335
339
|
# fetch consumptions for each consumptions gap in valid periods
|
|
340
|
+
start = max([gap["from"] + timedelta(hours=1), supply["date_start"]])
|
|
341
|
+
end = min([gap["to"], supply["date_end"]])
|
|
342
|
+
_LOGGER.info(
|
|
343
|
+
"%s: request consumptions from %s to %s",
|
|
344
|
+
self._scups,
|
|
345
|
+
start.isoformat(),
|
|
346
|
+
end.isoformat(),
|
|
347
|
+
)
|
|
336
348
|
self.update_consumptions(
|
|
337
349
|
cups,
|
|
338
350
|
distributor_code,
|
|
339
|
-
|
|
340
|
-
|
|
351
|
+
start,
|
|
352
|
+
end,
|
|
341
353
|
"0",
|
|
342
354
|
point_type,
|
|
343
355
|
)
|
|
344
356
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
for x in miss_maxim
|
|
349
|
-
if not (
|
|
350
|
-
date_to < contract["date_start"] or date_from > contract["date_end"]
|
|
351
|
-
)
|
|
352
|
-
]:
|
|
357
|
+
# update maximeter
|
|
358
|
+
for gap in miss_maxim:
|
|
359
|
+
if not (date_to < supply["date_start"] or date_from > supply["date_end"]):
|
|
353
360
|
# fetch maximeter for each maximeter gap in valid periods
|
|
354
361
|
start = max(
|
|
355
|
-
[gap["from"],
|
|
362
|
+
[gap["from"], supply["date_start"] + relativedelta(months=1)]
|
|
356
363
|
)
|
|
357
|
-
end = min([gap["to"],
|
|
364
|
+
end = min([gap["to"], supply["date_end"]])
|
|
358
365
|
start = min([start, end])
|
|
366
|
+
_LOGGER.info(
|
|
367
|
+
"%s: request maximeter from %s to %s",
|
|
368
|
+
self._scups,
|
|
369
|
+
start.isoformat(),
|
|
370
|
+
end.isoformat(),
|
|
371
|
+
)
|
|
359
372
|
self.update_maximeter(cups, distributor_code, start, end)
|
|
360
373
|
|
|
361
|
-
|
|
362
|
-
if oldest_contract != supply_date_start and oldest_contract > max(
|
|
363
|
-
[date_from, supply_date_start]
|
|
364
|
-
):
|
|
365
|
-
_LOGGER.info(
|
|
366
|
-
"%s: supplies and contract start date do not match, exploring non-registered contracts",
|
|
367
|
-
self._scups,
|
|
368
|
-
)
|
|
369
|
-
start = max([supply_date_start, date_from])
|
|
370
|
-
self.update_consumptions(
|
|
371
|
-
cups, distributor_code, start, oldest_contract, "0", point_type
|
|
372
|
-
)
|
|
373
|
-
self.update_maximeter(cups, distributor_code, start, oldest_contract)
|
|
374
|
-
miss_cons, miss_maxim = sort_and_filter(start, date_to)
|
|
375
|
-
else:
|
|
376
|
-
miss_cons, miss_maxim = sort_and_filter(
|
|
377
|
-
max([date_from, oldest_contract]), date_to
|
|
378
|
-
)
|
|
374
|
+
miss_cons, miss_maxim = sort_and_filter(date_from, date_to)
|
|
379
375
|
|
|
380
376
|
return True
|
|
381
377
|
|
e-data-1.2.18/PKG-INFO
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: e-data
|
|
3
|
-
Version: 1.2.18
|
|
4
|
-
Summary: Python library for managing spanish energy data from various web providers
|
|
5
|
-
Home-page: https://github.com/uvejota/python-edata
|
|
6
|
-
Author: VMG
|
|
7
|
-
Author-email: vmayorg@outlook.es
|
|
8
|
-
License: GPLv3
|
|
9
|
-
Description:
|
|
10
|
-
[](https://pepy.tech/project/e-data)
|
|
11
|
-
[](https://pepy.tech/project/e-data)
|
|
12
|
-
[](https://pepy.tech/project/e-data)
|
|
13
|
-
|
|
14
|
-
# python-edata
|
|
15
|
-
|
|
16
|
-
Este paquete proporciona herramientas para la descarga de tus datos de consumo eléctrico (desde Datadis.es) y su posterior procesado. La motivación principal es que conocer el consumo puede ayudarnos a reducirlo, e incluso a elegir una tarifa que mejor se adapte a nuestras necesidades. A día de hoy sus capacidades de facturación (€) son limitadas, soporta PVPC (según disponibilidad de datos de REData) y tarificación fija por tramos. Es el corazón de la integración [homeassistant-edata](https://github.com/uvejota/homeassistant-edata).
|
|
17
|
-
|
|
18
|
-
_**Esta herramienta no mantiene ningún tipo de vinculación con los proveedores de datos anteriormente mencionados, simplemente consulta la información disponible y facilita su posterior análisis.**_
|
|
19
|
-
|
|
20
|
-
## Instalación
|
|
21
|
-
|
|
22
|
-
Puedes instalar la última versión estable mediante:
|
|
23
|
-
|
|
24
|
-
``` bash
|
|
25
|
-
pip install e-data
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Si quieres probar la versión `dev` o contribuir a su desarrollo, clona este repositorio e instala manualmente las dependencias:
|
|
29
|
-
|
|
30
|
-
``` bash
|
|
31
|
-
pip install -r requirements.txt
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Estructura
|
|
35
|
-
|
|
36
|
-
El paquete consta de tres módulos diferenciados:
|
|
37
|
-
|
|
38
|
-
* **Conectores** (módulo `connectors`), para definir los métodos de consulta a los diferentes proveedores: Datadis y REData.
|
|
39
|
-
* **Procesadores** (módulo `processors`), para procesar datos de consumo, maxímetro, o coste (tarificación). Ahora mismo consta de tres procesadores: `billing`, `consumption` y `maximeter`, además de algunas utilidades ubicadas en `utils`. Los procesadores deben heredar de la clase Processor definida en `base.py`
|
|
40
|
-
* **Ayudantes** (módulo `helpers`), para ayudar en el uso y gestión de los anteriores, presentando de momento un único ayudante llamado `EdataHelper` que te permite recopilar `X` días de datos (por defecto 365) y automáticamente procesarlos. Los datos son almacenados en la variable `data`, mientras que los atributos autocalculados son almacenados en la variable `attributes`. Por lo general, primero utilizan los conectores y luego procesan los datos, gestionando varias tareas de recuperación (principalmente para Datadis).
|
|
41
|
-
|
|
42
|
-
Estos módulos corresponden a la siguiente estructura del paquete:
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
edata/
|
|
46
|
-
· __init__.py
|
|
47
|
-
· connectors/
|
|
48
|
-
· __init__.py
|
|
49
|
-
· datadis.py
|
|
50
|
-
· redata.py
|
|
51
|
-
· processors/
|
|
52
|
-
· __init__.py
|
|
53
|
-
· base.py
|
|
54
|
-
· billing.py
|
|
55
|
-
· consumption.py
|
|
56
|
-
· maximeter.py
|
|
57
|
-
· utils.py
|
|
58
|
-
· helpers.py
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Ejemplo de uso
|
|
62
|
-
|
|
63
|
-
Partimos de que tenemos credenciales en Datadis.es. Algunas aclaraciones:
|
|
64
|
-
* No es necesario solicitar API pública en el registro (se utilizará la API privada habilitada por defecto)
|
|
65
|
-
* El username suele ser el NIF del titular
|
|
66
|
-
* Copie el CUPS de la web de Datadis, algunas comercializadoras adhieren caracteres adicionales en el CUPS mostrado en su factura.
|
|
67
|
-
* La herramienta acepta el uso de NIF autorizado para consultar el suministro de otro titular.
|
|
68
|
-
|
|
69
|
-
``` python
|
|
70
|
-
from datetime import datetime
|
|
71
|
-
import json
|
|
72
|
-
|
|
73
|
-
# importamos definiciones de datos que nos interesen
|
|
74
|
-
from edata.definitions import PricingRules
|
|
75
|
-
# importamos el ayudante
|
|
76
|
-
from edata.helpers import EdataHelper
|
|
77
|
-
# importamos el procesador de utilidades
|
|
78
|
-
from edata.processors import utils
|
|
79
|
-
|
|
80
|
-
# Preparar reglas de tarificación (si se quiere)
|
|
81
|
-
PRICING_RULES_PVPC = PricingRules(
|
|
82
|
-
p1_kw_year_eur=30.67266,
|
|
83
|
-
p2_kw_year_eur=1.4243591,
|
|
84
|
-
meter_month_eur=0.81,
|
|
85
|
-
market_kw_year_eur=3.113,
|
|
86
|
-
electricity_tax=1.0511300560,
|
|
87
|
-
iva_tax=1.05,
|
|
88
|
-
# podemos rellenar los siguientes campos si quisiéramos precio fijo (y no pvpc)
|
|
89
|
-
p1_kwh_eur=None,
|
|
90
|
-
p2_kwh_eur=None,
|
|
91
|
-
p3_kwh_eur=None,
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
# Instanciar el helper
|
|
95
|
-
# 'authorized_nif' permite indicar el NIF de la persona que nos autoriza a consultar su CUPS.
|
|
96
|
-
# 'data' permite "cargar" al helper datos anteriores (resultado edata.data de una ejecución anterior), para evitar volver a consultar los mismos.
|
|
97
|
-
edata = EdataHelper(
|
|
98
|
-
"datadis_user",
|
|
99
|
-
"datadis_password",
|
|
100
|
-
"cups",
|
|
101
|
-
datadis_authorized_nif=None,
|
|
102
|
-
pricing_rules=PRICING_RULES_PVPC, # si se le pasa None, no aplica tarificación
|
|
103
|
-
data=None, # aquí podríamos cargar datos anteriores
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
# Solicitar actualización de todo el histórico (se almacena en edata.data)
|
|
107
|
-
edata.update(date_from=datetime(1970, 1, 1), date_to=datetime.today())
|
|
108
|
-
|
|
109
|
-
# volcamos todo lo obtenido a un fichero
|
|
110
|
-
with open("backup.json", "w") as file:
|
|
111
|
-
json.dump(utils.serialize_dict(edata.data), file) # se puede utilizar deserialize_dict para la posterior lectura del backup
|
|
112
|
-
|
|
113
|
-
# Imprimir atributos
|
|
114
|
-
print(edata.attributes)
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Platform: UNKNOWN
|
|
118
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
119
|
-
Classifier: Programming Language :: Python
|
|
120
|
-
Classifier: Programming Language :: Python :: 3
|
|
121
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
122
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
123
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
124
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
125
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
126
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
127
|
-
Requires-Python: >=3.6.0
|
|
128
|
-
Description-Content-Type: text/markdown
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: e-data
|
|
3
|
-
Version: 1.2.18
|
|
4
|
-
Summary: Python library for managing spanish energy data from various web providers
|
|
5
|
-
Home-page: https://github.com/uvejota/python-edata
|
|
6
|
-
Author: VMG
|
|
7
|
-
Author-email: vmayorg@outlook.es
|
|
8
|
-
License: GPLv3
|
|
9
|
-
Description:
|
|
10
|
-
[](https://pepy.tech/project/e-data)
|
|
11
|
-
[](https://pepy.tech/project/e-data)
|
|
12
|
-
[](https://pepy.tech/project/e-data)
|
|
13
|
-
|
|
14
|
-
# python-edata
|
|
15
|
-
|
|
16
|
-
Este paquete proporciona herramientas para la descarga de tus datos de consumo eléctrico (desde Datadis.es) y su posterior procesado. La motivación principal es que conocer el consumo puede ayudarnos a reducirlo, e incluso a elegir una tarifa que mejor se adapte a nuestras necesidades. A día de hoy sus capacidades de facturación (€) son limitadas, soporta PVPC (según disponibilidad de datos de REData) y tarificación fija por tramos. Es el corazón de la integración [homeassistant-edata](https://github.com/uvejota/homeassistant-edata).
|
|
17
|
-
|
|
18
|
-
_**Esta herramienta no mantiene ningún tipo de vinculación con los proveedores de datos anteriormente mencionados, simplemente consulta la información disponible y facilita su posterior análisis.**_
|
|
19
|
-
|
|
20
|
-
## Instalación
|
|
21
|
-
|
|
22
|
-
Puedes instalar la última versión estable mediante:
|
|
23
|
-
|
|
24
|
-
``` bash
|
|
25
|
-
pip install e-data
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Si quieres probar la versión `dev` o contribuir a su desarrollo, clona este repositorio e instala manualmente las dependencias:
|
|
29
|
-
|
|
30
|
-
``` bash
|
|
31
|
-
pip install -r requirements.txt
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Estructura
|
|
35
|
-
|
|
36
|
-
El paquete consta de tres módulos diferenciados:
|
|
37
|
-
|
|
38
|
-
* **Conectores** (módulo `connectors`), para definir los métodos de consulta a los diferentes proveedores: Datadis y REData.
|
|
39
|
-
* **Procesadores** (módulo `processors`), para procesar datos de consumo, maxímetro, o coste (tarificación). Ahora mismo consta de tres procesadores: `billing`, `consumption` y `maximeter`, además de algunas utilidades ubicadas en `utils`. Los procesadores deben heredar de la clase Processor definida en `base.py`
|
|
40
|
-
* **Ayudantes** (módulo `helpers`), para ayudar en el uso y gestión de los anteriores, presentando de momento un único ayudante llamado `EdataHelper` que te permite recopilar `X` días de datos (por defecto 365) y automáticamente procesarlos. Los datos son almacenados en la variable `data`, mientras que los atributos autocalculados son almacenados en la variable `attributes`. Por lo general, primero utilizan los conectores y luego procesan los datos, gestionando varias tareas de recuperación (principalmente para Datadis).
|
|
41
|
-
|
|
42
|
-
Estos módulos corresponden a la siguiente estructura del paquete:
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
edata/
|
|
46
|
-
· __init__.py
|
|
47
|
-
· connectors/
|
|
48
|
-
· __init__.py
|
|
49
|
-
· datadis.py
|
|
50
|
-
· redata.py
|
|
51
|
-
· processors/
|
|
52
|
-
· __init__.py
|
|
53
|
-
· base.py
|
|
54
|
-
· billing.py
|
|
55
|
-
· consumption.py
|
|
56
|
-
· maximeter.py
|
|
57
|
-
· utils.py
|
|
58
|
-
· helpers.py
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Ejemplo de uso
|
|
62
|
-
|
|
63
|
-
Partimos de que tenemos credenciales en Datadis.es. Algunas aclaraciones:
|
|
64
|
-
* No es necesario solicitar API pública en el registro (se utilizará la API privada habilitada por defecto)
|
|
65
|
-
* El username suele ser el NIF del titular
|
|
66
|
-
* Copie el CUPS de la web de Datadis, algunas comercializadoras adhieren caracteres adicionales en el CUPS mostrado en su factura.
|
|
67
|
-
* La herramienta acepta el uso de NIF autorizado para consultar el suministro de otro titular.
|
|
68
|
-
|
|
69
|
-
``` python
|
|
70
|
-
from datetime import datetime
|
|
71
|
-
import json
|
|
72
|
-
|
|
73
|
-
# importamos definiciones de datos que nos interesen
|
|
74
|
-
from edata.definitions import PricingRules
|
|
75
|
-
# importamos el ayudante
|
|
76
|
-
from edata.helpers import EdataHelper
|
|
77
|
-
# importamos el procesador de utilidades
|
|
78
|
-
from edata.processors import utils
|
|
79
|
-
|
|
80
|
-
# Preparar reglas de tarificación (si se quiere)
|
|
81
|
-
PRICING_RULES_PVPC = PricingRules(
|
|
82
|
-
p1_kw_year_eur=30.67266,
|
|
83
|
-
p2_kw_year_eur=1.4243591,
|
|
84
|
-
meter_month_eur=0.81,
|
|
85
|
-
market_kw_year_eur=3.113,
|
|
86
|
-
electricity_tax=1.0511300560,
|
|
87
|
-
iva_tax=1.05,
|
|
88
|
-
# podemos rellenar los siguientes campos si quisiéramos precio fijo (y no pvpc)
|
|
89
|
-
p1_kwh_eur=None,
|
|
90
|
-
p2_kwh_eur=None,
|
|
91
|
-
p3_kwh_eur=None,
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
# Instanciar el helper
|
|
95
|
-
# 'authorized_nif' permite indicar el NIF de la persona que nos autoriza a consultar su CUPS.
|
|
96
|
-
# 'data' permite "cargar" al helper datos anteriores (resultado edata.data de una ejecución anterior), para evitar volver a consultar los mismos.
|
|
97
|
-
edata = EdataHelper(
|
|
98
|
-
"datadis_user",
|
|
99
|
-
"datadis_password",
|
|
100
|
-
"cups",
|
|
101
|
-
datadis_authorized_nif=None,
|
|
102
|
-
pricing_rules=PRICING_RULES_PVPC, # si se le pasa None, no aplica tarificación
|
|
103
|
-
data=None, # aquí podríamos cargar datos anteriores
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
# Solicitar actualización de todo el histórico (se almacena en edata.data)
|
|
107
|
-
edata.update(date_from=datetime(1970, 1, 1), date_to=datetime.today())
|
|
108
|
-
|
|
109
|
-
# volcamos todo lo obtenido a un fichero
|
|
110
|
-
with open("backup.json", "w") as file:
|
|
111
|
-
json.dump(utils.serialize_dict(edata.data), file) # se puede utilizar deserialize_dict para la posterior lectura del backup
|
|
112
|
-
|
|
113
|
-
# Imprimir atributos
|
|
114
|
-
print(edata.attributes)
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Platform: UNKNOWN
|
|
118
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
119
|
-
Classifier: Programming Language :: Python
|
|
120
|
-
Classifier: Programming Language :: Python :: 3
|
|
121
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
122
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
123
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
124
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
125
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
126
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
127
|
-
Requires-Python: >=3.6.0
|
|
128
|
-
Description-Content-Type: text/markdown
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|