hidroconta 1.0.0__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.
- hidroconta-1.0.0/PKG-INFO +82 -0
- hidroconta-1.0.0/README.md +66 -0
- hidroconta-1.0.0/hidroconta/__init__.py +7 -0
- hidroconta-1.0.0/hidroconta/api.py +232 -0
- hidroconta-1.0.0/hidroconta/endpoints.py +24 -0
- hidroconta-1.0.0/hidroconta/hist.py +8 -0
- hidroconta-1.0.0/hidroconta/time.py +6 -0
- hidroconta-1.0.0/hidroconta/types.py +25 -0
- hidroconta-1.0.0/hidroconta.egg-info/PKG-INFO +82 -0
- hidroconta-1.0.0/hidroconta.egg-info/SOURCES.txt +13 -0
- hidroconta-1.0.0/hidroconta.egg-info/dependency_links.txt +1 -0
- hidroconta-1.0.0/hidroconta.egg-info/requires.txt +3 -0
- hidroconta-1.0.0/hidroconta.egg-info/top_level.txt +1 -0
- hidroconta-1.0.0/setup.cfg +4 -0
- hidroconta-1.0.0/setup.py +22 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: hidroconta
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: API to easily access Demeter REST API provided by Hidroconta S.A.U.
|
|
5
|
+
Home-page: UNKNOWN
|
|
6
|
+
Author: JavierL
|
|
7
|
+
Author-email: javier.lopez@hidroconta.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Integrations-PythonAPI
|
|
16
|
+
API Python para facilitar el acceso a los endpoints de la interfaz REST de Demeter
|
|
17
|
+
|
|
18
|
+
La API permite la gestión de grandes cantidades de datos mediante la librerÃa Pandas, siempre y cuando se utilice la siguiente directiva en las llamadas a los métodos:
|
|
19
|
+
```
|
|
20
|
+
# Pandas = True returns a pandas dataframe instead a json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
La forma de usar la API es la siguiente:
|
|
24
|
+
|
|
25
|
+
- Importar los modulos deseados:
|
|
26
|
+
```
|
|
27
|
+
import demeterpy as demeter
|
|
28
|
+
import demeterapitypes as tp
|
|
29
|
+
import pandas as pd
|
|
30
|
+
import datetime
|
|
31
|
+
import endpoints
|
|
32
|
+
```
|
|
33
|
+
- Seleccionar el servidor con el cual se quiere comunicar (se puede modificar en todo momento):
|
|
34
|
+
```
|
|
35
|
+
# Set server
|
|
36
|
+
demeter.set_server(endpoints.Server.MAIN)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- Realizar el login en dicho servidor
|
|
40
|
+
```
|
|
41
|
+
# Login
|
|
42
|
+
demeter.login('USERNAME', 'PASSWORD')
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Una vez seguidos los anteriores pasos, se puede realizar cualquier consulta sobre el sistema.
|
|
46
|
+
Algunas de ellas son:
|
|
47
|
+
|
|
48
|
+
- Búsqueda
|
|
49
|
+
```
|
|
50
|
+
# Search
|
|
51
|
+
df = demeter.search(text='SAT', element_types=[tp.Element.COUNTER, tp.Element.ANALOG_INPUT, tp.Element.RTU], status=tp.Status.ENABLED, pandas=True)
|
|
52
|
+
print(df)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- Obtención de historicos
|
|
56
|
+
```
|
|
57
|
+
# Get historics
|
|
58
|
+
df = demeter.get_historics(start_date=datetime.datetime.now(), end_date=datetime.datetime.now(), element_ids=[1000], subtype=tp.AnalogInputHist.subtype, subcode=[tp.AnalogInputHist.subcode], pandas=True)
|
|
59
|
+
print(df)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- Obtención de elementos
|
|
63
|
+
```
|
|
64
|
+
# Get
|
|
65
|
+
df = demeter.get_rtus(element_id=17512, pandas=True)
|
|
66
|
+
print(df)
|
|
67
|
+
```
|
|
68
|
+
La API define también una excepción especial cuando la llamada al endpoint de Demeter no devuelve el resultado esperado.
|
|
69
|
+
La excepción de denomina "DemeterStatusCodeException" y contiene el código de error HTTP.
|
|
70
|
+
```
|
|
71
|
+
# Exception treatment
|
|
72
|
+
try:
|
|
73
|
+
df = demeter.get_rtus(element_id=17512, pandas=True)
|
|
74
|
+
except demeter.DemeterStatusCodeException as status_code:
|
|
75
|
+
print('Error {}'.format(status_code))
|
|
76
|
+
```
|
|
77
|
+
- Por último, se deberÃa hacer un logout en el servidor
|
|
78
|
+
```
|
|
79
|
+
demeter.logout()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Integrations-PythonAPI
|
|
2
|
+
API Python para facilitar el acceso a los endpoints de la interfaz REST de Demeter
|
|
3
|
+
|
|
4
|
+
La API permite la gestión de grandes cantidades de datos mediante la librería Pandas, siempre y cuando se utilice la siguiente directiva en las llamadas a los métodos:
|
|
5
|
+
```
|
|
6
|
+
# Pandas = True returns a pandas dataframe instead a json
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
La forma de usar la API es la siguiente:
|
|
10
|
+
|
|
11
|
+
- Importar los modulos deseados:
|
|
12
|
+
```
|
|
13
|
+
import demeterpy as demeter
|
|
14
|
+
import demeterapitypes as tp
|
|
15
|
+
import pandas as pd
|
|
16
|
+
import datetime
|
|
17
|
+
import endpoints
|
|
18
|
+
```
|
|
19
|
+
- Seleccionar el servidor con el cual se quiere comunicar (se puede modificar en todo momento):
|
|
20
|
+
```
|
|
21
|
+
# Set server
|
|
22
|
+
demeter.set_server(endpoints.Server.MAIN)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Realizar el login en dicho servidor
|
|
26
|
+
```
|
|
27
|
+
# Login
|
|
28
|
+
demeter.login('USERNAME', 'PASSWORD')
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Una vez seguidos los anteriores pasos, se puede realizar cualquier consulta sobre el sistema.
|
|
32
|
+
Algunas de ellas son:
|
|
33
|
+
|
|
34
|
+
- Búsqueda
|
|
35
|
+
```
|
|
36
|
+
# Search
|
|
37
|
+
df = demeter.search(text='SAT', element_types=[tp.Element.COUNTER, tp.Element.ANALOG_INPUT, tp.Element.RTU], status=tp.Status.ENABLED, pandas=True)
|
|
38
|
+
print(df)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- Obtención de historicos
|
|
42
|
+
```
|
|
43
|
+
# Get historics
|
|
44
|
+
df = demeter.get_historics(start_date=datetime.datetime.now(), end_date=datetime.datetime.now(), element_ids=[1000], subtype=tp.AnalogInputHist.subtype, subcode=[tp.AnalogInputHist.subcode], pandas=True)
|
|
45
|
+
print(df)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- Obtención de elementos
|
|
49
|
+
```
|
|
50
|
+
# Get
|
|
51
|
+
df = demeter.get_rtus(element_id=17512, pandas=True)
|
|
52
|
+
print(df)
|
|
53
|
+
```
|
|
54
|
+
La API define también una excepción especial cuando la llamada al endpoint de Demeter no devuelve el resultado esperado.
|
|
55
|
+
La excepción de denomina "DemeterStatusCodeException" y contiene el código de error HTTP.
|
|
56
|
+
```
|
|
57
|
+
# Exception treatment
|
|
58
|
+
try:
|
|
59
|
+
df = demeter.get_rtus(element_id=17512, pandas=True)
|
|
60
|
+
except demeter.DemeterStatusCodeException as status_code:
|
|
61
|
+
print('Error {}'.format(status_code))
|
|
62
|
+
```
|
|
63
|
+
- Por último, se debería hacer un logout en el servidor
|
|
64
|
+
```
|
|
65
|
+
demeter.logout()
|
|
66
|
+
```
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import requests as re
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import json
|
|
4
|
+
import hidroconta.types as tp
|
|
5
|
+
import hidroconta.hist as hist
|
|
6
|
+
import hidroconta.endpoints as endpoints
|
|
7
|
+
import hidroconta.time as time
|
|
8
|
+
import datetime
|
|
9
|
+
# For python <3.9, replace list[] with List[] from 'from typing import List'
|
|
10
|
+
|
|
11
|
+
'''
|
|
12
|
+
Allows an easy access to Demeter API from Python and
|
|
13
|
+
implements pandas dataframes compatibility
|
|
14
|
+
'''
|
|
15
|
+
|
|
16
|
+
'''Session cookies stored'''
|
|
17
|
+
__stored_cookies = None
|
|
18
|
+
|
|
19
|
+
POST_HDR = {"Content-Type": "application/json"}
|
|
20
|
+
|
|
21
|
+
# Exception to be thrown when Demeter API returns an error status code
|
|
22
|
+
class DemeterStatusCodeException(Exception):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def set_server(server: endpoints.Server):
|
|
26
|
+
endpoints.set_server(server)
|
|
27
|
+
|
|
28
|
+
def get_server():
|
|
29
|
+
return endpoints.get_server()
|
|
30
|
+
|
|
31
|
+
def login(username: str, password: str):
|
|
32
|
+
global __stored_cookies
|
|
33
|
+
|
|
34
|
+
payload = '{{"username":"{}", "password":"{}"}}'.format(username, password)
|
|
35
|
+
|
|
36
|
+
response = re.post(get_server() + endpoints.DEMETER_LOGIN ,data=payload, headers=POST_HDR)
|
|
37
|
+
|
|
38
|
+
if response.status_code != 200:
|
|
39
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
40
|
+
|
|
41
|
+
cookies = response.cookies.get_dict()
|
|
42
|
+
__stored_cookies = cookies
|
|
43
|
+
return cookies
|
|
44
|
+
|
|
45
|
+
def logout():
|
|
46
|
+
response = re.post(get_server() + endpoints.DEMETER_LOGOUT, cookies=__stored_cookies)
|
|
47
|
+
if response.status_code != 200:
|
|
48
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
49
|
+
|
|
50
|
+
def __get_elements(element: str, element_id: int = None, pandas = False):
|
|
51
|
+
print(get_server() + endpoints.DEMETER_GET + element + ('' if element_id == None else ('/' + str(element_id))))
|
|
52
|
+
response = re.get(get_server() + endpoints.DEMETER_GET + element + ('' if element_id == None else ('/' + str(element_id))), cookies=__stored_cookies)
|
|
53
|
+
if response.status_code != 200:
|
|
54
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
55
|
+
else:
|
|
56
|
+
if pandas:
|
|
57
|
+
data = json.loads(response.text)
|
|
58
|
+
return pd.json_normalize(data)
|
|
59
|
+
else:
|
|
60
|
+
return response.text
|
|
61
|
+
|
|
62
|
+
def search(text:str = None, element_types:list[tp.Element] = None, status:tp.Status = tp.Status.ALL, pandas = False):
|
|
63
|
+
|
|
64
|
+
payload = '{"status":"' + status + '"'
|
|
65
|
+
if(text is not None):
|
|
66
|
+
payload = payload + ',"searchText":"' + text + '"'
|
|
67
|
+
if(element_types is None):
|
|
68
|
+
element_types = [e for e in tp.Element.__dict__.values() if type(e) == str and e != 'demeterapitypes']
|
|
69
|
+
payload = payload + ',"type":' + str(element_types).replace("'", '"')
|
|
70
|
+
payload = payload + '}'
|
|
71
|
+
print(payload)
|
|
72
|
+
|
|
73
|
+
response = re.post(get_server() + endpoints.DEMETER_SEARCH, headers=POST_HDR, data=payload, cookies=__stored_cookies)
|
|
74
|
+
if response.status_code != 200:
|
|
75
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
76
|
+
else:
|
|
77
|
+
if pandas:
|
|
78
|
+
data = json.loads(response.text)
|
|
79
|
+
return pd.json_normalize(data)
|
|
80
|
+
else:
|
|
81
|
+
return response.text
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_rtus(element_id:int = None, pandas = False):
|
|
85
|
+
return __get_elements('rtus', element_id, pandas)
|
|
86
|
+
|
|
87
|
+
def get_counters(element_id:int = None, pandas = False):
|
|
88
|
+
return __get_elements('counters', element_id, pandas)
|
|
89
|
+
|
|
90
|
+
def get_analog_inputs(element_id:int = None, pandas = False):
|
|
91
|
+
return __get_elements('analogInputs', element_id, pandas)
|
|
92
|
+
|
|
93
|
+
def get_digital_inputs(element_id:int = None, pandas = False):
|
|
94
|
+
return __get_elements('digitalInputs', element_id, pandas)
|
|
95
|
+
|
|
96
|
+
def get_iris_nb(element_id:int = None, pandas = False):
|
|
97
|
+
return __get_elements('iris/nbiot', element_id, pandas)
|
|
98
|
+
|
|
99
|
+
def get_iris_lw(element_id:int = None, pandas = False):
|
|
100
|
+
return __get_elements('iris/lorawan', element_id, pandas)
|
|
101
|
+
|
|
102
|
+
def get_iris_sigfox(element_id:int = None, pandas = False):
|
|
103
|
+
return __get_elements('iris/sigfox', element_id, pandas)
|
|
104
|
+
|
|
105
|
+
def get_iris_gprs(element_id:int = None, pandas = False):
|
|
106
|
+
return __get_elements('iris/gprs', element_id, pandas)
|
|
107
|
+
|
|
108
|
+
def get_installations(element_id:int = None, pandas = False):
|
|
109
|
+
return __get_elements('installations', element_id, pandas)
|
|
110
|
+
|
|
111
|
+
def get_rtus_installation_dict(pandas = False):
|
|
112
|
+
installations = __get_elements('installations', pandas=pandas)
|
|
113
|
+
rtus = __get_elements('rtus', pandas=pandas)
|
|
114
|
+
match_installations = pd.merge(installations, rtus, on='installationId')
|
|
115
|
+
timezones = {}
|
|
116
|
+
for index, match in match_installations.iterrows():
|
|
117
|
+
timezones[match['rtuId']] = match['timeZone']
|
|
118
|
+
return timezones
|
|
119
|
+
|
|
120
|
+
def get_centinel(element_id:int = None, pandas = False):
|
|
121
|
+
return __get_elements('centinel', element_id, pandas)
|
|
122
|
+
|
|
123
|
+
def get_centaurus(element_id:int = None, pandas = False):
|
|
124
|
+
return __get_elements('centaurus', element_id, pandas)
|
|
125
|
+
|
|
126
|
+
def global_update(rtu_id: int, timestamp: datetime.datetime, liters:int):
|
|
127
|
+
|
|
128
|
+
payload = '{"rtuId":' + str(rtu_id) + ',"timestamp":"' + time.strftime_demeter(timestamp) + '","value":' + str(int(liters)) + '}'
|
|
129
|
+
print(payload)
|
|
130
|
+
response = re.put(get_server() + endpoints.DEMETER_UPDATE_COUNTER_GLOBAL ,data=payload, headers=POST_HDR, cookies=__stored_cookies)
|
|
131
|
+
if response.status_code != 200:
|
|
132
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
133
|
+
|
|
134
|
+
def add_historics(rtu_id: int, historics: list[hist.Hist]):
|
|
135
|
+
|
|
136
|
+
hist = '['
|
|
137
|
+
for historic in historics[:-1]:
|
|
138
|
+
hist = hist + '{'
|
|
139
|
+
hist = hist + '"timestamp":"' + str(historic.timestamp) + '",'
|
|
140
|
+
hist = hist + '"subtype":' + str(historic.type.subtype) + ','
|
|
141
|
+
hist = hist + '"subcode":' + str(historic.type.subcode) + ','
|
|
142
|
+
hist = hist + '"value":' + str(historic.value) + ','
|
|
143
|
+
hist = hist + '"position":' + str(historic.position) + ','
|
|
144
|
+
hist = hist + '"expansion":' + str(historic.expansion) + '},'
|
|
145
|
+
historic = historics[-1]
|
|
146
|
+
hist = hist + '{'
|
|
147
|
+
hist = hist + '"timestamp":"' + str(historic.timestamp) + '",'
|
|
148
|
+
hist = hist + '"subtype":' + str(historic.type.subtype) + ','
|
|
149
|
+
hist = hist + '"subcode":' + str(historic.type.subcode) + ','
|
|
150
|
+
hist = hist + '"value":' + str(historic.value) + ','
|
|
151
|
+
hist = hist + '"position":' + str(historic.position) + ','
|
|
152
|
+
hist = hist + '"expansion":' + str(historic.expansion) + '}'
|
|
153
|
+
hist = hist + ']'
|
|
154
|
+
|
|
155
|
+
payload = '{{"rtuId":{}, "historicDataEntities":{}}}'.format(rtu_id, hist)
|
|
156
|
+
print(payload)
|
|
157
|
+
response = re.post(get_server() + endpoints.DEMETER_HISTORICS ,data=payload, headers=POST_HDR, cookies=__stored_cookies)
|
|
158
|
+
if response.status_code != 200:
|
|
159
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
160
|
+
|
|
161
|
+
def get_historics(start_date: datetime.datetime, end_date: datetime.datetime, element_ids: list[int], subtype: int, subcode:list[int] = [], pandas=False):
|
|
162
|
+
start_date = time.strftime_demeter(start_date)
|
|
163
|
+
end_date = time.strftime_demeter(end_date)
|
|
164
|
+
payload = '{{"from":"{}", "until":"{}", "subcode":{}, "subtype":{}, "elementIds":{}}}'.format(start_date, end_date, subcode, subtype, element_ids)
|
|
165
|
+
|
|
166
|
+
response = re.post(get_server() + endpoints.DEMETER_HISTORY_DATA, headers=POST_HDR, data=payload, cookies=__stored_cookies)
|
|
167
|
+
if response.status_code != 200:
|
|
168
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
169
|
+
else:
|
|
170
|
+
if pandas:
|
|
171
|
+
data = json.loads(response.text)
|
|
172
|
+
return pd.json_normalize(data)
|
|
173
|
+
else:
|
|
174
|
+
return response.text
|
|
175
|
+
|
|
176
|
+
def get_minute_consumption(start_date: datetime.datetime, end_date: datetime.datetime, element_ids: list[int], period_value: int, min_interval: bool = True, pandas=False):
|
|
177
|
+
start_date = time.strftime_demeter(start_date)
|
|
178
|
+
end_date = time.strftime_demeter(end_date)
|
|
179
|
+
payload = '{{"from":"{}", "until":"{}", "minInterval":{}, "elementIds":{}, "periodValue":{}}}'.format(start_date, end_date, min_interval, element_ids, period_value)
|
|
180
|
+
|
|
181
|
+
response = re.post(get_server() + endpoints.DEMETER_CONSUMPTION, headers=POST_HDR, data=payload, cookies=__stored_cookies)
|
|
182
|
+
if response.status_code != 200:
|
|
183
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
184
|
+
else:
|
|
185
|
+
if pandas:
|
|
186
|
+
data = json.loads(response.text)
|
|
187
|
+
unfolded = pd.DataFrame(columns=['code', 'timestamp'])
|
|
188
|
+
df = pd.json_normalize(data)
|
|
189
|
+
for ix, row in df.iterrows():
|
|
190
|
+
unfolded_row = row['values']
|
|
191
|
+
unfolded_list = []
|
|
192
|
+
for x in unfolded_row:
|
|
193
|
+
x['code'] = row['series'][0]
|
|
194
|
+
x['values'] = x['values'][0]
|
|
195
|
+
unfolded_list.append(x)
|
|
196
|
+
x['timestamp'] = time.strptime_demeter(x['timestamp'])
|
|
197
|
+
unfolded = pd.concat([unfolded, pd.json_normalize(unfolded_list)])
|
|
198
|
+
return unfolded
|
|
199
|
+
else:
|
|
200
|
+
return response.text
|
|
201
|
+
|
|
202
|
+
def update_analog_input_value(rtu_id:int, position:int, expansion:int, value:int, timestamp:datetime.datetime):
|
|
203
|
+
timestamp = time.strftime_demeter(timestamp)
|
|
204
|
+
payload = '{{"rtuId":{}, "position":{}, "expansion":{}, "value":{}, "timestamp":"{}"}}'.format(rtu_id, position, expansion, value, timestamp)
|
|
205
|
+
print(payload)
|
|
206
|
+
response = re.put(get_server() + endpoints.DEMETER_ANALOG_UPDATE ,data=payload, headers=POST_HDR, cookies=__stored_cookies)
|
|
207
|
+
if response.status_code != 200:
|
|
208
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
209
|
+
|
|
210
|
+
def create_iris_nb(payload:str):
|
|
211
|
+
response = re.post(get_server() + endpoints.DEMETER_IRIS_NB, data=payload, headers=POST_HDR, cookies=__stored_cookies)
|
|
212
|
+
if response.status_code != 201:
|
|
213
|
+
print(response.text)
|
|
214
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
215
|
+
|
|
216
|
+
def delete_iris_nb(elementid:int, confirmation = True):
|
|
217
|
+
if confirmation:
|
|
218
|
+
iris = get_iris_nb(element_id=elementid, pandas=True)
|
|
219
|
+
print('{} será eliminado de {}.'.format(iris['code'].values.tolist()[0], get_server()))
|
|
220
|
+
res = input('Desea continuar (y|n)? ')
|
|
221
|
+
if res.capitalize() == 'Y':
|
|
222
|
+
print('Borrando')
|
|
223
|
+
response = re.delete(get_server() + endpoints.DEMETER_IRIS_NB + '/' + str(elementid), cookies=__stored_cookies)
|
|
224
|
+
if response.status_code != 200:
|
|
225
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
226
|
+
else:
|
|
227
|
+
print('Cancelado')
|
|
228
|
+
return
|
|
229
|
+
else:
|
|
230
|
+
response = re.delete(get_server() + endpoints.DEMETER_IRIS_NB + '/' + str(elementid), cookies=__stored_cookies)
|
|
231
|
+
if response.status_code != 200:
|
|
232
|
+
raise DemeterStatusCodeException(response.status_code)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
DEMETER_LOGIN = 'login'
|
|
2
|
+
DEMETER_LOGOUT = 'logout'
|
|
3
|
+
DEMETER_GET = ''
|
|
4
|
+
DEMETER_UPDATE_COUNTER_GLOBAL = 'counters/global/update'
|
|
5
|
+
DEMETER_HISTORICS = 'historics'
|
|
6
|
+
DEMETER_HISTORY_DATA = 'history/data'
|
|
7
|
+
DEMETER_ANALOG_UPDATE = 'analogInputs/value/update'
|
|
8
|
+
DEMETER_SEARCH = 'search'
|
|
9
|
+
DEMETER_CONSUMPTION = 'history/consumption/minute/volume'
|
|
10
|
+
DEMETER_IRIS_LORAWAN = 'iris/lorawan'
|
|
11
|
+
DEMETER_IRIS_NB = 'iris/nbiot'
|
|
12
|
+
|
|
13
|
+
class Server(enumerate):
|
|
14
|
+
MAIN = 'https://demeter2.hidroconta.com/Demeter2/v2/'
|
|
15
|
+
TEST = 'https://demeter2-test.hidroconta.com/Demeter2/v2/'
|
|
16
|
+
|
|
17
|
+
__server = Server.MAIN
|
|
18
|
+
|
|
19
|
+
def set_server(server: Server):
|
|
20
|
+
global __server
|
|
21
|
+
__server = server
|
|
22
|
+
|
|
23
|
+
def get_server():
|
|
24
|
+
return __server
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class AnalogInputHist:
|
|
2
|
+
subtype = 4
|
|
3
|
+
subcode = 0
|
|
4
|
+
|
|
5
|
+
class CounterGlobalHist:
|
|
6
|
+
subtype = 2
|
|
7
|
+
subcode = 2
|
|
8
|
+
|
|
9
|
+
class Element(enumerate):
|
|
10
|
+
ANALOG_INPUT = 'analogInputs'
|
|
11
|
+
COUNTER = 'counters'
|
|
12
|
+
RTU = 'rtus'
|
|
13
|
+
IRIS = 'iris'
|
|
14
|
+
HYDRANT = 'hydrants'
|
|
15
|
+
VALVE = 'valves'
|
|
16
|
+
DIGITAL_INPUT = 'digitalInputs'
|
|
17
|
+
DIGITAL_OUTPUT = 'digitalOutputs'
|
|
18
|
+
CENTINEL = 'centinels'
|
|
19
|
+
WMBUS_COUNTER = 'wmbusCounters'
|
|
20
|
+
CENTAURUS = 'centaurus'
|
|
21
|
+
|
|
22
|
+
class Status(enumerate):
|
|
23
|
+
ENABLED = 'enabled'
|
|
24
|
+
DISABLED = 'disabled'
|
|
25
|
+
ALL = 'all'
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: hidroconta
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: API to easily access Demeter REST API provided by Hidroconta S.A.U.
|
|
5
|
+
Home-page: UNKNOWN
|
|
6
|
+
Author: JavierL
|
|
7
|
+
Author-email: javier.lopez@hidroconta.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Integrations-PythonAPI
|
|
16
|
+
API Python para facilitar el acceso a los endpoints de la interfaz REST de Demeter
|
|
17
|
+
|
|
18
|
+
La API permite la gestión de grandes cantidades de datos mediante la librerÃa Pandas, siempre y cuando se utilice la siguiente directiva en las llamadas a los métodos:
|
|
19
|
+
```
|
|
20
|
+
# Pandas = True returns a pandas dataframe instead a json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
La forma de usar la API es la siguiente:
|
|
24
|
+
|
|
25
|
+
- Importar los modulos deseados:
|
|
26
|
+
```
|
|
27
|
+
import demeterpy as demeter
|
|
28
|
+
import demeterapitypes as tp
|
|
29
|
+
import pandas as pd
|
|
30
|
+
import datetime
|
|
31
|
+
import endpoints
|
|
32
|
+
```
|
|
33
|
+
- Seleccionar el servidor con el cual se quiere comunicar (se puede modificar en todo momento):
|
|
34
|
+
```
|
|
35
|
+
# Set server
|
|
36
|
+
demeter.set_server(endpoints.Server.MAIN)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- Realizar el login en dicho servidor
|
|
40
|
+
```
|
|
41
|
+
# Login
|
|
42
|
+
demeter.login('USERNAME', 'PASSWORD')
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Una vez seguidos los anteriores pasos, se puede realizar cualquier consulta sobre el sistema.
|
|
46
|
+
Algunas de ellas son:
|
|
47
|
+
|
|
48
|
+
- Búsqueda
|
|
49
|
+
```
|
|
50
|
+
# Search
|
|
51
|
+
df = demeter.search(text='SAT', element_types=[tp.Element.COUNTER, tp.Element.ANALOG_INPUT, tp.Element.RTU], status=tp.Status.ENABLED, pandas=True)
|
|
52
|
+
print(df)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- Obtención de historicos
|
|
56
|
+
```
|
|
57
|
+
# Get historics
|
|
58
|
+
df = demeter.get_historics(start_date=datetime.datetime.now(), end_date=datetime.datetime.now(), element_ids=[1000], subtype=tp.AnalogInputHist.subtype, subcode=[tp.AnalogInputHist.subcode], pandas=True)
|
|
59
|
+
print(df)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- Obtención de elementos
|
|
63
|
+
```
|
|
64
|
+
# Get
|
|
65
|
+
df = demeter.get_rtus(element_id=17512, pandas=True)
|
|
66
|
+
print(df)
|
|
67
|
+
```
|
|
68
|
+
La API define también una excepción especial cuando la llamada al endpoint de Demeter no devuelve el resultado esperado.
|
|
69
|
+
La excepción de denomina "DemeterStatusCodeException" y contiene el código de error HTTP.
|
|
70
|
+
```
|
|
71
|
+
# Exception treatment
|
|
72
|
+
try:
|
|
73
|
+
df = demeter.get_rtus(element_id=17512, pandas=True)
|
|
74
|
+
except demeter.DemeterStatusCodeException as status_code:
|
|
75
|
+
print('Error {}'.format(status_code))
|
|
76
|
+
```
|
|
77
|
+
- Por último, se deberÃa hacer un logout en el servidor
|
|
78
|
+
```
|
|
79
|
+
demeter.logout()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
hidroconta/__init__.py
|
|
4
|
+
hidroconta/api.py
|
|
5
|
+
hidroconta/endpoints.py
|
|
6
|
+
hidroconta/hist.py
|
|
7
|
+
hidroconta/time.py
|
|
8
|
+
hidroconta/types.py
|
|
9
|
+
hidroconta.egg-info/PKG-INFO
|
|
10
|
+
hidroconta.egg-info/SOURCES.txt
|
|
11
|
+
hidroconta.egg-info/dependency_links.txt
|
|
12
|
+
hidroconta.egg-info/requires.txt
|
|
13
|
+
hidroconta.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hidroconta
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name='hidroconta',
|
|
5
|
+
version='1.0.0',
|
|
6
|
+
packages=['hidroconta'],
|
|
7
|
+
install_requires=[
|
|
8
|
+
'pandas',
|
|
9
|
+
'requests',
|
|
10
|
+
'datetime'
|
|
11
|
+
],
|
|
12
|
+
author='JavierL',
|
|
13
|
+
author_email='javier.lopez@hidroconta.com',
|
|
14
|
+
description='API to easily access Demeter REST API provided by Hidroconta S.A.U.',
|
|
15
|
+
long_description=open('README.md').read(),
|
|
16
|
+
long_description_content_type='text/markdown',
|
|
17
|
+
classifiers=[
|
|
18
|
+
'Programming Language :: Python :: 3',
|
|
19
|
+
'License :: OSI Approved :: MIT License',
|
|
20
|
+
'Operating System :: OS Independent',
|
|
21
|
+
],
|
|
22
|
+
)
|