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.
Files changed (27) hide show
  1. e_data-1.2.19/PKG-INFO +136 -0
  2. e_data-1.2.19/e_data.egg-info/PKG-INFO +136 -0
  3. {e-data-1.2.18 → e_data-1.2.19}/edata/connectors/datadis.py +13 -9
  4. {e-data-1.2.18 → e_data-1.2.19}/edata/definitions.py +1 -3
  5. {e-data-1.2.18 → e_data-1.2.19}/edata/helpers.py +63 -67
  6. {e-data-1.2.18 → e_data-1.2.19}/setup.py +1 -1
  7. e-data-1.2.18/PKG-INFO +0 -128
  8. e-data-1.2.18/e_data.egg-info/PKG-INFO +0 -128
  9. {e-data-1.2.18 → e_data-1.2.19}/LICENSE +0 -0
  10. {e-data-1.2.18 → e_data-1.2.19}/MANIFEST.in +0 -0
  11. {e-data-1.2.18 → e_data-1.2.19}/README.md +0 -0
  12. {e-data-1.2.18 → e_data-1.2.19}/e_data.egg-info/SOURCES.txt +0 -0
  13. {e-data-1.2.18 → e_data-1.2.19}/e_data.egg-info/dependency_links.txt +0 -0
  14. {e-data-1.2.18 → e_data-1.2.19}/e_data.egg-info/requires.txt +0 -0
  15. {e-data-1.2.18 → e_data-1.2.19}/e_data.egg-info/top_level.txt +0 -0
  16. {e-data-1.2.18 → e_data-1.2.19}/edata/__init__.py +0 -0
  17. {e-data-1.2.18 → e_data-1.2.19}/edata/connectors/__init__.py +0 -0
  18. {e-data-1.2.18 → e_data-1.2.19}/edata/connectors/redata.py +0 -0
  19. {e-data-1.2.18 → e_data-1.2.19}/edata/const.py +0 -0
  20. {e-data-1.2.18 → e_data-1.2.19}/edata/processors/__init__.py +0 -0
  21. {e-data-1.2.18 → e_data-1.2.19}/edata/processors/base.py +0 -0
  22. {e-data-1.2.18 → e_data-1.2.19}/edata/processors/billing.py +0 -0
  23. {e-data-1.2.18 → e_data-1.2.19}/edata/processors/consumption.py +0 -0
  24. {e-data-1.2.18 → e_data-1.2.19}/edata/processors/maximeter.py +0 -0
  25. {e-data-1.2.18 → e_data-1.2.19}/edata/processors/utils.py +0 -0
  26. {e-data-1.2.18 → e_data-1.2.19}/edata/storage.py +0 -0
  27. {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
+ [![Downloads](https://pepy.tech/badge/e-data)](https://pepy.tech/project/e-data)
32
+ [![Downloads](https://pepy.tech/badge/e-data/month)](https://pepy.tech/project/e-data)
33
+ [![Downloads](https://pepy.tech/badge/e-data/week)](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
+ [![Downloads](https://pepy.tech/badge/e-data)](https://pepy.tech/project/e-data)
32
+ [![Downloads](https://pepy.tech/badge/e-data/month)](https://pepy.tech/project/e-data)
33
+ [![Downloads](https://pepy.tech/badge/e-data/week)](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.info("Local cache updated")
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
- self._update_recent_queries(url + params, response)
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
- self._update_recent_queries(url + params)
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
- self._update_recent_queries(url + params)
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
- self._update_recent_queries(url + params)
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[-5:]
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
- """Synchronous update."""
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
- """Synchronous data update of supplies."""
147
+ """Update supplies."""
142
148
 
143
- _LOGGER.info("%s: supplies update triggered", self._scups)
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
- """Synchronous data update of contracts."""
164
+ """Update contracts."""
159
165
 
160
- _LOGGER.info("%s: contracts update triggered", self._scups)
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
- """Synchronous data update of consumptions."""
191
+ """Update consumptions."""
186
192
 
187
- _LOGGER.info("%s: consumptions update triggered", self._scups)
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
- """Synchronous data update of maximeter."""
219
+ """Update maximeter."""
213
220
 
214
- _LOGGER.info("%s: maximeter update triggered", self._scups)
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
- """Synchronous data update."""
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, please check configured CUPS", self._scups
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 look for gaps
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
- oldest_contract = datetime.today()
322
- for contract in self.data["contracts"]:
323
- # register oldest contract
324
- if contract["date_start"] < oldest_contract:
325
- oldest_contract = contract["date_start"]
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
- max([gap["from"] + timedelta(hours=1), contract["date_start"]]),
340
- min([gap["to"], contract["date_end"]]),
351
+ start,
352
+ end,
341
353
  "0",
342
354
  point_type,
343
355
  )
344
356
 
345
- # update maximeter
346
- for gap in [
347
- x
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"], contract["date_start"] + relativedelta(months=1)]
362
+ [gap["from"], supply["date_start"] + relativedelta(months=1)]
356
363
  )
357
- end = min([gap["to"], contract["date_end"]])
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
- # safe check periods in non-registered contracts
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
 
@@ -20,7 +20,7 @@ URL = "https://github.com/uvejota/python-edata"
20
20
  EMAIL = "vmayorg@outlook.es"
21
21
  AUTHOR = "VMG"
22
22
  REQUIRES_PYTHON = ">=3.6.0"
23
- VERSION = "1.2.18"
23
+ VERSION = "1.2.19"
24
24
 
25
25
  # What packages are required for this module to be executed?
26
26
  REQUIRED = [
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
- [![Downloads](https://pepy.tech/badge/e-data)](https://pepy.tech/project/e-data)
11
- [![Downloads](https://pepy.tech/badge/e-data/month)](https://pepy.tech/project/e-data)
12
- [![Downloads](https://pepy.tech/badge/e-data/week)](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
- [![Downloads](https://pepy.tech/badge/e-data)](https://pepy.tech/project/e-data)
11
- [![Downloads](https://pepy.tech/badge/e-data/month)](https://pepy.tech/project/e-data)
12
- [![Downloads](https://pepy.tech/badge/e-data/week)](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